# Redux to RTK Upgrade Plan ## Current State Analysis ### ✅ What's Working Well 1. **Modular Organization**: Reducers organized by features (server, sessions, chatbot, etc.) 2. **Redux Toolkit Integration**: Using `@reduxjs/toolkit` and `configureStore` 3. **Consistent Loading States**: Pattern for `loading/loaded` on each entity 4. **TypeScript Integration**: Well-defined types for actions and state ### Current Architecture Pattern ``` features/ ├── server/ │ ├── actionTypes.ts - Action type constants │ ├── actionCreators.ts - Async action creators with thunks │ ├── reducer.ts - State management logic │ └── api.ts - API calls └── [other features...] ``` ## Upgrade Opportunities ### 1. Redux Toolkit Slices (Major Improvement) **Current approach** uses classic Redux pattern with separate files: ```typescript // actionTypes.ts export const LOAD_SERVER_DATA_SUCCESS = "LOAD_SERVER_DATA_SUCCESS" as const; // actionCreators.ts export function loadServerData() { return async function(dispatch: Dispatch): Promise { try { const data = await dispatch(sendHttpRequest(api.getServerData()) as any); dispatch({ type: types.LOAD_SERVER_DATA_SUCCESS, payload: data }); } catch (error) { throw error; } }; } // reducer.ts export default function serverReducer(state: ServerState = initialState.server, action: ServerAction): ServerState { switch (action.type) { case types.LOAD_SERVER_DATA_SUCCESS: return { ...state, data: { ...action.payload, loading: false, loaded: true } }; default: return state; } } ``` **Recommended RTK Slice approach**: ```typescript // serverSlice.ts import { createSlice, createAsyncThunk } from '@reduxjs/toolkit'; export const loadServerData = createAsyncThunk( 'server/loadData', async () => { return await api.getServerData(); } ); const serverSlice = createSlice({ name: 'server', initialState: { data: { loading: false, loaded: false }, activeSession: { loading: false, loaded: false } }, reducers: { // Synchronous actions clearServerData: (state) => { state.data = { loading: false, loaded: false }; } }, extraReducers: (builder) => { builder .addCase(loadServerData.pending, (state) => { state.data.loading = true; }) .addCase(loadServerData.fulfilled, (state, action) => { state.data = { ...action.payload, loading: false, loaded: true }; }) .addCase(loadServerData.rejected, (state) => { state.data.loading = false; }); } }); export const { clearServerData } = serverSlice.actions; export default serverSlice.reducer; ``` **Benefits**: - 70% less boilerplate code - Automatic action creators - Built-in loading/error handling - Immer integration (direct state mutations) - Better TypeScript inference ### 2. RTK Query for API Management **Current API pattern**: ```typescript // Separate API calls, manual caching, manual loading states export function loadServerData() { return async function(dispatch: Dispatch): Promise { try { const data = await dispatch(sendHttpRequest(api.getServerData()) as any); dispatch({ type: types.LOAD_SERVER_DATA_SUCCESS, payload: data }); } catch (error) { throw error; } }; } ``` **RTK Query approach**: ```typescript // api/serverApi.ts import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react'; export const serverApi = createApi({ reducerPath: 'serverApi', baseQuery: fetchBaseQuery({ baseUrl: '/api/', prepareHeaders: (headers) => { // Add auth headers, content-type, etc. return headers; }, }), tagTypes: ['ServerData'], endpoints: (builder) => ({ getServerData: builder.query({ query: () => 'server', providesTags: ['ServerData'], }), getActiveSession: builder.query({ query: (sessionId) => `sessions/${sessionId}`, providesTags: ['ServerData'], }), }), }); export const { useGetServerDataQuery, useGetActiveSessionQuery } = serverApi; ``` **Usage in components**: ```typescript function ServerComponent() { const { data: serverData, isLoading, error } = useGetServerDataQuery(); if (isLoading) return ; if (error) return ; return ; } ``` **Benefits**: - Automatic caching and cache invalidation - Built-in loading/error states - Automatic re-fetching - Optimistic updates - Request deduplication - Background sync ### 3. Enhanced Type Safety **Current types**: ```typescript interface LoadServerDataSuccessAction { type: typeof types.LOAD_SERVER_DATA_SUCCESS; payload: any; // ❌ Too generic } ``` **Improved types**: ```typescript interface ServerData { hostname: string; sessionsCount: number; isChainMember: boolean; domain: { name: string; }; // ... specific fields instead of 'any' } interface ActiveSession { sessionId: string; isActive: boolean; forwards: Forward[]; // ... specific structure } ``` ### 4. Selectors with Reselect **Current approach**: ```typescript // Direct state access in components const serverData = useSelector(state => state.server.data); ``` **Memoized selectors**: ```typescript import { createSelector } from '@reduxjs/toolkit'; // Base selectors export const selectServerState = (state: RootState) => state.server; export const selectServerData = (state: RootState) => state.server.data; // Memoized computed selectors export const selectIsServerLoading = createSelector( selectServerData, (serverData) => serverData.loading ); export const selectServerSummary = createSelector( selectServerData, (serverData) => ({ hostname: serverData.hostname, status: serverData.isChainMember ? 'Active' : 'Inactive', sessions: serverData.sessionsCount }) ); ``` ## Migration Strategy ### Phase 1: Setup RTK Query Infrastructure 1. Install RTK Query dependencies 2. Configure store with RTK Query middleware 3. Create base API slice ### Phase 2: Migrate One Feature (Server Module) 1. Convert server module to RTK Query 2. Update components to use RTK Query hooks 3. Remove old Redux actions/reducers 4. Test thoroughly ### Phase 3: Gradual Migration 1. Migrate one feature at a time 2. Sessions → Forwards → System → Charts 3. Keep both patterns during transition ### Phase 4: Cleanup 1. Remove old HTTP action utilities 2. Consolidate remaining slices 3. Update all TypeScript types ## Benefits of Upgrade ### Developer Experience - **90% less boilerplate** for API calls - **Automatic TypeScript inference** - **Better debugging tools** - **Standardized patterns** ### Performance - **Automatic request deduplication** - **Smart caching strategies** - **Background updates** - **Optimistic updates** ### Maintenance - **Single source of truth for API logic** - **Automatic error handling** - **Built-in retry logic** - **Cache invalidation strategies** ## Implementation Priority 1. **High Impact, Low Risk**: RTK Query for new API endpoints 2. **Medium Impact**: Convert existing simple GET endpoints 3. **High Impact, Higher Risk**: Complex state management with mutations 4. **Polish**: Enhanced selectors and type safety ## Timeline Estimate - **Phase 1** (Setup): 1-2 days - **Phase 2** (First module): 2-3 days - **Phase 3** (Gradual migration): 1-2 weeks - **Phase 4** (Cleanup): 2-3 days **Total**: 3-4 weeks for complete migration ## Files to Reference Current key files in the Redux architecture: - `src/redux/configureStore.ts` - Store configuration - `src/redux/reducers/index.ts` - Root reducer - `src/features/*/actionTypes.ts` - Action constants - `src/features/*/actionCreators.ts` - Thunk actions - `src/features/*/reducer.ts` - State management - `src/features/*/api.ts` - API calls These will be consolidated into: - `src/store/store.ts` - RTK configured store - `src/api/` - RTK Query API slices - `src/features/*/slice.ts` - RTK slices for local state ## Notes for Future Implementation - Start with the server module as it has the simplest data flow - Keep backward compatibility during transition - Use TypeScript strict mode to catch type issues early - Consider using RTK Query code generation for OpenAPI specs if available - Plan for cache persistence if needed for offline functionality ## Decision Points - **Do we need offline support?** → Affects caching strategy - **Are there real-time updates needed?** → Consider WebSocket integration - **How complex are the mutations?** → Affects optimistic update strategy - **Do we need request cancellation?** → RTK Query provides this automatically This upgrade will significantly modernize the Redux architecture while maintaining all current functionality.