State management
redux-toolkit/ · hooks/redux/ · hooks/react-query/ · hooks/utils/ · services/
:::info Rule of thumb Redux = small, persisted, synchronous client state. React Query = all server-derived data and caching. Context = ergonomic derivation of that data (session authz, date/time). Plain hooks + the event emitter = ephemeral or cross-cutting concerns that don't belong in any store. :::
Redux Toolkit (redux-toolkit/)
| Slice | State | Notes |
|---|---|---|
globalSlice | counter, openSidebar, sidebarCollapsed | Pure UI/layout state — sidebar open/collapsed toggles. |
userSlice | isLoggedIn, userData | Legacy login snapshot; largely superseded by SessionContextProvider. |
permissionsSlice | permissions: string[] | Bootstrap/fallback permission cache, seeded from SSR cookies then continuously overwritten by live session-context data. Explicitly deprecated for authorization decisions. |
Persistence: store.ts wraps the combined reducer with redux-persist, using a custom encrypted storage engine (encryptedStorage.ts) — AES via crypto-js, keyed by NEXT_APP_CR_SECRET_KEY — instead of plain localStorage, so the entire persisted state tree is opaque at rest.
hooks/redux/useAppDispatch.tsx / useAppSelector.tsx are the canonical typed wrappers around react-redux's hooks — used app-wide instead of the untyped originals.
React Query configuration
Configured inline in pages/_app.tsx (no separate client file):
new QueryClient({
defaultOptions: {
queries: {
refetchOnWindowFocus: false,
retry: 0,
staleTime: 30_000, // 30s
gcTime: 300_000, // 5 min
},
},
});
Tuned for a backend in eu-north-1 (~188ms RTT from the US) — the comment in source explains call sites needing live data should explicitly opt out with staleTime: 0 (e.g. risk/money-sensitive queries).
Utility hooks & event bus
| Hook | Purpose |
|---|---|
useDebounce | Debounced copy of a value (search inputs). |
useToggle | {state, toggle, handleopen, handleclose} for modals/drawers. |
useOnScreen | IntersectionObserver wrapper for viewport visibility. |
useDetectOnline | Online/offline toast on connectivity change. |
useEventEmitter + useLatestRef | Subscribes a callback to the shared eventemitter3 bus without stale closures. |
useCustomMediaQuery | Breakpoints beyond MUI defaults — large/2K/4K/8K screen support for the dashboard. |
useResizeHook | vw-based pixel sizing for chart/canvas layout. |
services/event.emitter.ts is a single shared eventemitter3 instance, mounted via components/EventListener/EventListener.tsx in _app.tsx, used for cross-cutting notifications (e.g. toast-on-error from the axios interceptor) that need to reach arbitrary components without prop drilling.