Redux - Architecture & Best Practices

Recommended structure (by feature)

src/
  app/
    store.ts
    hooks.ts
  features/
    todos/
      todosSlice.ts
      TodosList.tsx
      TodoItem.tsx
      selectors.ts
    users/
      usersSlice.ts
      selectors.ts
  shared/
    components/
    lib/

Try it: Feature slice boundaries

View source
const { createSlice, configureStore } = RTK;
const todosSlice = createSlice({
  name:'todos',
  initialState: [{id:1,text:'Learn Redux',done:false}],
  reducers:{
    add(state, a){ state.push({id:Date.now(), text:a.payload, done:false}); },
    toggle(state, a){ const t=state.find(x=>x.id===a.payload); if(t) t.done=!t.done; }
  }
});
const usersSlice = createSlice({ name:'users', initialState:[{id:1,name:'Ada'}], reducers:{} });
const store = configureStore({ reducer:{ todos: todosSlice.reducer, users: usersSlice.reducer } });
function App(){
  const { Provider, useSelector, useDispatch } = ReactRedux;
  function Todos(){
    const items = useSelector(s=>s.todos);
    const dispatch = useDispatch();
    const [text,setText] = React.useState('');
    return (
      <div>
        <input value={text} onChange={e=>setText(e.target.value)} placeholder="Add" />
        <button onClick={()=>{ if(text.trim()){dispatch(todosSlice.actions.add(text)); setText('');}}}>Add</button>
        <ul>{items.map(t=> <li key={t.id}><label><input type="checkbox" checked={t.done} onChange={()=>dispatch(todosSlice.actions.toggle(t.id))}/> {t.text}</label></li>)}</ul>
      </div>
    );
  }
  return <Provider store={store}><Todos /></Provider> 
}
ReactDOM.createRoot(document.getElementById('arch-root')).render(<App />);

Best practices

  • Organize by feature; co-locate slice, selectors, and components.
  • Keep reducers pure; async in thunks or RTK Query.
  • Use memoized selectors to keep components efficient.