reverse-proxy-frontend/docs/redux-rtk-upgrade-plan.md
Tudor Stanciu c05de1a7dc Merged PR 108: Frontend full upgrade and migration to Typescript
- feat: Add session management components and improve system overview
- feat: Update dependencies and replace react-flags with react-country-flag
- Update dependencies in package.json: reintroduce react-dom and upgrade redux to version 5.0.1
- refactor: update chatbot implementation and dependencies
- refactor: migrate to Redux Toolkit and update dependencies
- feat: enhance ReactCountryFlag component with SVG support
- refactor: remove Bootstrap dependency and update Node engine requirement; add LabelValue component for better UI consistency
- refactor: enhance LabelValue component usage in ServerSummary for improved readability and tooltip support
- refactor: replace inline text with LabelValue component in ActiveSessionSummary and SessionSummary for improved consistency and readability
- refactor: update components to use LabelValue for improved consistency and readability
- refactor: optimize LabelValue component for improved readability and structure
- refactor: improve code readability in SessionForwardsComponent by standardizing arrow function syntax and adjusting styling properties
2025-09-27 23:24:55 +00:00

323 lines
8.6 KiB
Markdown

# 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<void> {
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<void> {
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<ServerData, void>({
query: () => 'server',
providesTags: ['ServerData'],
}),
getActiveSession: builder.query<ActiveSession, string>({
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 <Spinner />;
if (error) return <ErrorMessage />;
return <ServerDisplay data={serverData} />;
}
```
**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.