Back
$cat ./enterprise-state-management-redux-toolkit-rtk-query.md

Predictable State at Enterprise Scale: Redux Toolkit, RTK Query & Optimistic Cache Synchronization

S
Surinder Singh•August 10, 2026•3 min read
ReduxTypeScriptState ManagementReactFrontend
Predictable State at Enterprise Scale: Redux Toolkit, RTK Query & Optimistic Cache Synchronization

Taming State in High-Frequency React Applications

In complex frontend applications—such as real-time VoIP dialers, interactive canvas editors, and trading dashboards—uncontrolled state mutations lead to UI lag, race conditions, and unmaintainable code.

Redux Toolkit (RTK) paired with RTK Query provides an architecture for managing both global client-side UI state and synchronized server cache data.


1. Normalized State with createEntityAdapter

Instead of storing arrays of items that require $O(n)$ searches and expensive full-array updates, RTK's createEntityAdapter normalizes data into an { ids: [], entities: {} } lookup table:

typescript
import { createEntityAdapter, createSlice } from '@reduxjs/toolkit';

export interface CallLog {
  id: string;
  callerNumber: string;
  durationSeconds: number;
  status: 'COMPLETED' | 'MISSED' | 'IN_PROGRESS';
  timestamp: string;
}

export const callLogsAdapter = createEntityAdapter<CallLog>({
  selectId: (log) => log.id,
  sortComparer: (a, b) => b.timestamp.localeCompare(a.timestamp),
});

export const callLogsSlice = createSlice({
  name: 'callLogs',
  initialState: callLogsAdapter.getInitialState({ isDialing: false }),
  reducers: {
    addCallLog: callLogsAdapter.addOne,
    updateCallStatus: callLogsAdapter.updateOne,
    removeCallLog: callLogsAdapter.removeOne,
    setDialingState(state, action) {
      state.isDialing = action.payload;
    },
  },
});

2. Optimistic UI Updates with RTK Query

For instant user feedback, RTK Query allows you to update the local cached state immediately before the network mutation finishes, automatically rolling back if the request fails:

typescript
import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react';

export const portfolioApi = createApi({
  reducerPath: 'portfolioApi',
  baseQuery: fetchBaseQuery({ baseUrl: '/api/' }),
  tagTypes: ['Projects'],
  endpoints: (builder) => ({
    toggleProjectStar: builder.mutation<{ success: boolean }, { id: number; isStarred: boolean }>({
      query: ({ id, isStarred }) => ({
        url: `projects/${id}/star`,
        method: 'POST',
        body: { isStarred },
      }),
      async onQueryStarted({ id, isStarred }, { dispatch, queryFulfilled }) {
        // Optimistically update the UI cache
        const patchResult = dispatch(
          portfolioApi.util.updateQueryData('getProjects' as any, undefined, (draft: any) => {
            const project = draft.find((p: any) => p.id === id);
            if (project) project.is_starred = isStarred;
          })
        );
        try {
          await queryFulfilled;
        } catch {
          // Revert cache if server rejected
          patchResult.undo();
        }
      },
    }),
  }),
});

3. Real-Time WebSocket Redux Middleware

Connecting incoming WebSocket messages directly to Redux actions keeps the UI reactive while keeping components purely presentational:

typescript
import { Middleware } from '@reduxjs/toolkit';
import { callLogsSlice } from './call-logs-slice';

export const webSocketMiddleware: Middleware = (store) => {
  let socket: WebSocket | null = null;

  return (next) => (action: any) => {
    if (action.type === 'ws/connect') {
      socket = new WebSocket(action.payload.url);
      socket.onmessage = (event) => {
        const message = JSON.parse(event.data);
        if (message.type === 'CALL_UPDATE') {
          store.dispatch(callLogsSlice.actions.updateCallStatus(message.data));
        }
      };
    }
    return next(action);
  };
};

4. Best Practice Summary

  1. Keep Component State Local: Only hoist state to Redux when it is shared across multiple disjoint subtrees.
  2. Use Normalized Entity Adapters: Prevents expensive re-renders and simplifies CRUD modifications.
  3. Use Optimistic Updates: Makes user actions feel instantaneous regardless of network latency.
Explore more articles→

Discussion (0)

Technical insights, critiques, and feedback

Verifying authentication status...

No comments yet. Be the first to start the discussion!