feat(redux): state management chapter (RTK) - #3
Open
Demonkratiy wants to merge 18 commits into
Open
Conversation
Restore the state management theory page (Flux, glossary, library landscape, decision tree) authored before the FSD detour. Update stale App references to HomePage and align the chapter plan with FSD slice placement (todoSlice in entities/todo/model, filterSlice in features/filter-todos/model, store in app/). Add README entry.
Install @reduxjs/toolkit and react-redux. Add app/store.ts (configureStore + RootState/AppDispatch), app/hooks.ts (typed useAppSelector/useAppDispatch), wrap the app in <Provider>, and add entities/todo/model/todoSlice.ts with todoAdded/todoToggled/todoDeleted. Add a documented boundaries exception so any layer may import @/app/store and @/app/hooks (the store is cross-cutting infra). Components are not wired to the store yet.
Explain RTK install, configureStore, typed hooks, Provider, the first slice (createSlice + Immer + prepare), FSD placement and the app-store boundary exception with the reducer-injection alternative noted.
Replace HomePage's local todos useState with useAppSelector(state => state.todos.items) and dispatch todoAdded/todoToggled/todoDeleted via useAppDispatch. The todos source of truth now lives in Redux (inspectable in DevTools). Filter stays on useState until the next step. Leaf components remain prop-driven; prop drilling removal comes later.
Add a section on how to see the whole store statically (store.ts as the domain map, RootState as the full shape, slice files as per-domain single files, index.ts as public API). Refresh the 'what's next' section now that todos is wired to the store.
Add features/filter-todos/model/filterSlice.ts with a filterChanged reducer over { status: FilterStatus }, and expose filterReducer/filterChanged through the feature public API.
Register filterReducer in the store (state.filter) and replace HomePage's filter useState with useAppSelector(state => state.filter.status) + dispatch(filterChanged). Both todos and filter now live in Redux.
Add redux-concepts.md explaining actions, payload (incl. payload vs state shape and prepare), Immer as a standalone library embedded in RTK, and entity-vs-feature action ownership. Link it from README and the setup page.
Add selectTodos (entities/todo), selectFilterStatus and a memoized selectFilteredTodos (features/filter-todos, via createSelector composing selectTodos + selectFilterStatus). Expose them through the slice public APIs.
Replace HomePage's inline todos filtering with useAppSelector(selectFilteredTodos) and read the filter via selectFilterStatus. The derivation logic and knowledge of store shape now live in the selectors, not the component.
Add a section clarifying that the reducer's state is the local slice while a selector's state is the global RootState, where the mount key comes from, and field-vs-reducer anatomy.
Extract rootReducer via combineReducers and add setupStore(preloadedState) so tests and Storybook get isolated stores. Add a global Storybook decorator wrapping every story in a fresh Provider, seedable per-story via parameters.preloadedState.
AddTodoForm, TodoFilter and TodoList read/dispatch via typed hooks instead of props (TodoItem stays presentational). Stories use the Provider decorator and preloadedState. Removes prop drilling from these components.
With features/widget connected to the store, HomePage no longer owns state, handlers or selectors — it just composes AddTodoForm, TodoFilter and TodoList with no props.
Document selectors (basic vs derived, createSelector memoization, FSD placement) in redux-concepts. Add connected-vs-presentational layer guidance and a Storybook-for-connected-components section (setupStore, Provider decorator, preloadedState) to redux-setup.
Compare the same todo domain across Redux (RTK), Zustand and MobX with illustrative snippets, a differences table and when-to-pick guidance. Zustand/MobX are not implemented in the project — documentation only.
Replace the three separately-connected components (AddTodoForm, TodoFilter, TodoList) with a single connected TodoWidget driven by one facade hook (useTodoWidget). AddTodoForm/TodoFilter are presentational again (props-only); widgets/todo-list is removed.
Add widget-facade-hook.md (composite-hook rationale, end-to-end Redux trace, MobX caveat) and react-hooks-fundamentals.md (rules of hooks mechanism, custom hooks, hook-call vs returned-values). Link both from README and update the fsd-architecture.md TodoList note.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
State management chapter (Redux Toolkit)
Introduces global state management with Redux Toolkit, migrating the TODO domain from local
useStateto a Redux store, on top of the FSD structure.What changed (by step)
app/store.ts(configureStore+setupStorefactory +RootState/AppDispatch), typed hooksapp/hooks.ts,<Provider>inapp/main.tsx.entities/todo/model/todoSlice.ts(todoAdded/todoToggled/todoDeleted, Immer), wired intoHomePage.features/filter-todos/model/filterSlice.ts(filterChanged).selectTodos,selectFilterStatus, and a memoizedselectFilteredTodos(createSelector).AddTodoForm,TodoFilter,TodoListconnect to the store via hooks;TodoItemstays presentational;HomePagereduces to pure composition.Architecture notes
app/; a documented boundaries exception (rule1cineslint.config.js) lets any layer import@/app/storeand@/app/hooks(cross-cutting infra). Cleaner reducer-injection alternative noted in the wiki.shared/ui+entitiesstay dumb (Storybook-friendly);features/widgetsare connected.setupStore+ per-storyparameters.preloadedState(real store, not a mock).Docs (wiki, Russian)
state-management.md— Flux theory, glossary, decision treeredux-setup.md— store, hooks, Provider, slice, static store overview, connected-vs-presentational, Storybook+Reduxredux-concepts.md— actions, payload, Immer, reducer-local vs selector-global, selectors, entity-vs-featurestate-managers-comparison.md— same domain in Redux vs Zustand vs MobX (docs only; not implemented)Verification
npm run build✅npm run lint✅ (FSD boundaries enforced)npm run build-storybook✅npm run format:check✅