Component ──dispatch(action)──▶ Reducer ──▶ New State ──▶ Selector ──▶ Component
│ ▲
│ │
▼ (memoized via
autoSaveMiddleware createSelector)
│
▼
localStorage
Normalized State Shape (each entity slice):
┌─────────────────────────────────┐
│ { byId: { "node-1": {...}, │ ◀── O(1) lookup by ID
│ "node-2": {...} }, │
│ allIds: ["node-1","node-2"], │ ◀── Iteration order
│ selected: ["node-1"] } │ ◀── Multi-select tracking
└─────────────────────────────────┘
Code Review Phase 4: Redux Store, Slices & Selectors
Purpose: Review the state management layer — the nervous system of the app. Every slice follows the same normalized
{ byId, allIds }pattern, and selectors usecreateSelectorfor memoization.Prerequisites: Phases 1-3 (Types, Constants, Domain Layer)
Estimated time: 2 hours
Files: 20
Architecture Diagram
Redux Data Flow:
Files to Review (in order)
Store Setup
src/store/hooks.ts— TypeduseAppDispatchanduseAppSelectorhookssrc/store/index.ts— Store configuration with 7 reducers + middlewaresrc/store/middleware/autoSaveMiddleware.ts— Debounced auto-save to localStorageFeature Slices
src/features/project/projectSlice.ts— Project CRUDsrc/features/project/projectSelectors.ts— Project selectorssrc/features/nodes/nodesSlice.ts— Node CRUD + selection managementsrc/features/nodes/nodesSelectors.ts— Memoized node selectorssrc/features/datasets/datasetsSlice.ts— Dataset CRUD + selectionsrc/features/datasets/datasetsSelectors.ts— Memoized dataset selectorssrc/features/connections/connectionsSlice.ts— Connection CRUDsrc/features/connections/connectionsSelectors.ts— Connection selectorssrc/features/ui/uiSlice.ts— UI state (modals, panels, tutorial, walkthrough)src/features/ui/uiSelectors.ts— UI selectorssrc/features/validation/validationSlice.ts— Validation results storagesrc/features/validation/validationSelectors.ts— Validation selectorssrc/features/theme/themeSlice.ts— Theme togglesrc/features/canvas/canvasSelectors.ts— Combined selectors with Set-based optimizationTests
src/features/nodes/nodesSlice.test.ts— 14 tests: node reducer mutations (add, update, delete, select)src/features/datasets/datasetsSlice.test.ts— 14 tests: dataset reducer mutationssrc/store/middleware/preferencesMiddleware.test.ts— 5 tests: auto-save middleware trigger/debounce behaviorsrc/store/middleware/autoSaveMiddleware.test.ts— 3 tests: trigger, skip, and debounce behaviorKey Concepts
createSliceuses Immer under the hood, so "mutating" code likestate.byId[id] = nodeis actually producing immutable updatespreparecallback inaddNode/addDatasetenables action creators to accept flexible inputs and normalize them before reaching the reducercreateSelectorfrom RTK memoizes selectors — they only recompute when input selectors return new referencesselectSelectedNodeIdsSetcreates aSetfrom the array for O(1) lookups in hot render pathsFocus Areas
uiSlice.tsno longer callslocalStorage.setItem()directly — persistence is handled byautoSaveMiddleware.themeSlice.tsonly reads localStorage at initialization time viasafeGetItem(STORAGE_KEYS.THEME). Confirm no new side effects have crept in.uiSliceandthemeSlicenow useSTORAGE_KEYSconstants. Verify consistency.deleteNodesanddeleteDatasetsnow useSetfor O(n) complexity. Verify the Set-based implementation is correct.createSelector-based selectors properly memoized when called with inline parameters?autoSaveMiddlewareuses module-levellet saveTimeout— is this safe with hot module replacement?