The official, opinionated, batteries-included toolset for efficient Redux development
—
Redux Toolkit provides React-specific enhancements and utilities available from @reduxjs/toolkit/react that extend the core functionality with React-optimized features.
React-enhanced version of dynamic middleware with component-specific hook factories and context integration.
/**
* React-enhanced dynamic middleware with hook factories
* Available from @reduxjs/toolkit/react
*/
function createDynamicMiddleware<
State = any,
DispatchType extends Dispatch<AnyAction> = Dispatch<AnyAction>
>(): ReactDynamicMiddlewareInstance<State, DispatchType>;
interface ReactDynamicMiddlewareInstance<State, DispatchType> extends DynamicMiddlewareInstance<State, DispatchType> {
/** Creates hook factory for specific React context */
createDispatchWithMiddlewareHookFactory(context?: React.Context<any>): CreateDispatchWithMiddlewareHook<State, DispatchType>;
/** Default hook factory for current React context */
createDispatchWithMiddlewareHook: CreateDispatchWithMiddlewareHook<State, DispatchType>;
}
interface CreateDispatchWithMiddlewareHook<State, DispatchType> {
/** Create dispatch function with additional middleware */
(middleware: Middleware<any, State, DispatchType>[]): DispatchType;
}Usage Examples:
import { createDynamicMiddleware } from '@reduxjs/toolkit/react';
import { Provider } from 'react-redux';
// Create React-enhanced dynamic middleware
const dynamicMiddleware = createDynamicMiddleware<RootState, AppDispatch>();
const store = configureStore({
reducer: rootReducer,
middleware: (getDefaultMiddleware) =>
getDefaultMiddleware().concat(dynamicMiddleware.middleware)
});
// Component-specific middleware
const AnalyticsComponent = () => {
const dispatch = dynamicMiddleware.createDispatchWithMiddlewareHook([
analyticsMiddleware,
performanceMiddleware
]);
const handleAction = () => {
// This dispatch goes through analytics and performance middleware
dispatch(trackableAction('button_click'));
};
return <button onClick={handleAction}>Track This Click</button>;
};
// Feature-specific middleware
const FeatureComponent = () => {
const dispatchWithFeatureMiddleware = dynamicMiddleware.createDispatchWithMiddlewareHook([
featureLoggingMiddleware,
featureValidationMiddleware
]);
const handleFeatureAction = () => {
dispatchWithFeatureMiddleware(featureAction({ data: 'test' }));
};
return <button onClick={handleFeatureAction}>Feature Action</button>;
};
// Context-specific middleware factory
const CustomContext = React.createContext<any>(null);
const ContextualComponent = () => {
const createContextualDispatch = dynamicMiddleware.createDispatchWithMiddlewareHookFactory(CustomContext);
const dispatch = createContextualDispatch([contextSpecificMiddleware]);
return (
<CustomContext.Provider value={/* contextual data */}>
<button onClick={() => dispatch(contextAction())}>
Contextual Action
</button>
</CustomContext.Provider>
);
};Advanced patterns for integrating Redux Toolkit with React components and hooks.
/**
* Type-safe hooks for Redux Toolkit integration
*/
interface TypedReduxHooks<RootState, AppDispatch> {
useAppDispatch: () => AppDispatch;
useAppSelector: TypedUseSelectorHook<RootState>;
}
/**
* Custom hook factory for component-specific middleware
*/
type MiddlewareHookFactory<State, DispatchType> = (
middleware: Middleware<any, State, DispatchType>[]
) => DispatchType;Usage Examples:
import { TypedUseSelectorHook, useDispatch, useSelector } from 'react-redux';
import type { RootState, AppDispatch } from './store';
// Typed hooks setup
export const useAppDispatch = () => useDispatch<AppDispatch>();
export const useAppSelector: TypedUseSelectorHook<RootState> = useSelector;
// Component with typed hooks
const TypedComponent = () => {
const dispatch = useAppDispatch(); // Fully typed dispatch
const user = useAppSelector(state => state.auth.user); // Typed selector
const handleLogin = () => {
dispatch(loginUser({ username: 'test' })); // Type-checked action
};
return (
<div>
{user ? `Welcome ${user.name}` : 'Please log in'}
<button onClick={handleLogin}>Login</button>
</div>
);
};
// Custom hook for feature-specific middleware
const useFeatureDispatch = () => {
return dynamicMiddleware.createDispatchWithMiddlewareHook([
featureMiddleware,
debugMiddleware
]);
};
const FeatureComponent = () => {
const featureDispatch = useFeatureDispatch();
return (
<button onClick={() => featureDispatch(featureAction())}>
Feature Action with Custom Middleware
</button>
);
};
// Conditional middleware based on component props
const ConditionalMiddlewareComponent = ({ enableAnalytics }: { enableAnalytics: boolean }) => {
const middleware = useMemo(() => {
const middlewares: Middleware[] = [loggingMiddleware];
if (enableAnalytics) {
middlewares.push(analyticsMiddleware);
}
return middlewares;
}, [enableAnalytics]);
const dispatch = dynamicMiddleware.createDispatchWithMiddlewareHook(middleware);
return (
<button onClick={() => dispatch(trackedAction())}>
{enableAnalytics ? 'Tracked Action' : 'Regular Action'}
</button>
);
};Integrate Redux Toolkit with React component lifecycles for automatic cleanup and state management.
/**
* Hook for component-scoped listeners
*/
interface ComponentListenerHook<StateType, DispatchType> {
(options: {
actionCreator?: any;
matcher?: ActionMatcher<any>;
effect: ListenerEffect<any, StateType, DispatchType, any>;
}): void;
}Usage Examples:
import { useEffect, useRef } from 'react';
import { createListenerMiddleware } from '@reduxjs/toolkit';
// Component-scoped listener middleware
const ComponentWithListeners = () => {
const listenerMiddleware = useRef(createListenerMiddleware()).current;
const dispatch = useAppDispatch();
useEffect(() => {
// Add component-specific listeners
const unsubscribe = listenerMiddleware.startListening({
actionCreator: componentAction,
effect: async (action, listenerApi) => {
// Component-specific side effects
console.log('Component action:', action.payload);
// Access component context through closure
// Perform component-related operations
}
});
// Cleanup on unmount
return () => {
unsubscribe();
listenerMiddleware.clearListeners();
};
}, [listenerMiddleware]);
const handleAction = () => {
dispatch(componentAction({ data: 'component data' }));
};
return <button onClick={handleAction}>Trigger Component Action</button>;
};
// HOC for automatic middleware injection
const withMiddleware = <P extends object>(
Component: React.ComponentType<P>,
middleware: Middleware[]
) => {
return (props: P) => {
const dispatch = dynamicMiddleware.createDispatchWithMiddlewareHook(middleware);
// Provide enhanced dispatch through context or props
return <Component {...props} dispatch={dispatch} />;
};
};
// Usage of HOC
const EnhancedComponent = withMiddleware(BaseComponent, [
analyticsMiddleware,
loggingMiddleware
]);
// Hook for automatic listener cleanup
const useComponentListener = (
actionCreator: any,
effect: (action: any) => void,
deps: React.DependencyList = []
) => {
const listenerRef = useRef<() => void>();
useEffect(() => {
// Remove previous listener
listenerRef.current?.();
// Add new listener
listenerRef.current = listenerMiddleware.startListening({
actionCreator,
effect: (action, listenerApi) => effect(action)
});
// Cleanup function
return () => {
listenerRef.current?.();
};
}, deps);
};
// Component using automatic listeners
const AutoListenerComponent = ({ userId }: { userId: string }) => {
useComponentListener(
userUpdated,
(action) => {
if (action.payload.id === userId) {
console.log('Current user updated:', action.payload);
}
},
[userId]
);
return <div>Component with automatic listeners</div>;
};Middleware that can access React context values for enhanced functionality.
/**
* Context-aware middleware factory
*/
interface ContextAwareMiddlewareFactory<ContextType> {
(context: ContextType): Middleware;
}Usage Examples:
// Theme-aware middleware
const ThemeContext = React.createContext<{ theme: 'light' | 'dark' }>({ theme: 'light' });
const createThemeAwareMiddleware = (theme: 'light' | 'dark'): Middleware =>
(store) => (next) => (action) => {
// Add theme information to actions
if (action.type.startsWith('ui/')) {
return next({
...action,
meta: {
...action.meta,
theme
}
});
}
return next(action);
};
const ThemedComponent = () => {
const { theme } = useContext(ThemeContext);
const dispatch = dynamicMiddleware.createDispatchWithMiddlewareHook([
createThemeAwareMiddleware(theme),
uiLoggingMiddleware
]);
return (
<button onClick={() => dispatch(uiAction('button_click'))}>
Themed Button
</button>
);
};
// User context-aware middleware
const UserContext = React.createContext<{ user: User | null }>({ user: null });
const createUserAwareMiddleware = (user: User | null): Middleware =>
(store) => (next) => (action) => {
// Add user context to all actions
const enhancedAction = {
...action,
meta: {
...action.meta,
userId: user?.id,
userRole: user?.role,
timestamp: Date.now()
}
};
return next(enhancedAction);
};
const UserAwareComponent = () => {
const { user } = useContext(UserContext);
const dispatch = dynamicMiddleware.createDispatchWithMiddlewareHook([
createUserAwareMiddleware(user),
auditMiddleware
]);
return (
<button onClick={() => dispatch(userAction('profile_update'))}>
Update Profile
</button>
);
};
// Multi-context middleware
const AppContexts = {
theme: ThemeContext,
user: UserContext,
feature: FeatureContext
};
const useContextAwareDispatch = () => {
const theme = useContext(ThemeContext);
const user = useContext(UserContext);
const features = useContext(FeatureContext);
return dynamicMiddleware.createDispatchWithMiddlewareHook([
createThemeAwareMiddleware(theme.theme),
createUserAwareMiddleware(user.user),
createFeatureAwareMiddleware(features.enabledFeatures)
]);
};
const MultiContextComponent = () => {
const dispatch = useContextAwareDispatch();
return (
<button onClick={() => dispatch(contextualAction())}>
Context-Aware Action
</button>
);
};React-specific patterns for optimizing Redux Toolkit performance in React applications.
/**
* Optimized selector patterns
*/
interface OptimizedSelectorPatterns {
/** Memoized selector with shallow equality */
shallowEqual: (a: any, b: any) => boolean;
/** Selector with custom equality function */
customEqual: (equalityFn: (a: any, b: any) => boolean) => any;
}Usage Examples:
import { shallowEqual } from 'react-redux';
import { createSelector } from '@reduxjs/toolkit';
// Optimized selectors to prevent unnecessary re-renders
const selectOptimizedUserData = createSelector(
[(state: RootState) => state.user.profile],
(profile) => ({
name: profile?.name,
email: profile?.email,
avatar: profile?.avatar
})
);
const OptimizedUserComponent = () => {
// Use shallow equality to prevent re-renders when reference changes but values don't
const userData = useAppSelector(selectOptimizedUserData, shallowEqual);
return (
<div>
<img src={userData.avatar} alt={userData.name} />
<span>{userData.name}</span>
</div>
);
};
// Memoized component with Redux integration
const MemoizedPostItem = React.memo(({ postId }: { postId: string }) => {
const post = useAppSelector(
state => state.posts.entities[postId],
shallowEqual
);
if (!post) return null;
return (
<article>
<h3>{post.title}</h3>
<p>{post.content}</p>
</article>
);
});
// Virtualized list with Redux integration
const VirtualizedPostsList = () => {
const postIds = useAppSelector(state => state.posts.ids);
const renderItem = useCallback(({ index }: { index: number }) => {
return <MemoizedPostItem key={postIds[index]} postId={postIds[index]} />;
}, [postIds]);
return (
<VirtualList
height={400}
itemCount={postIds.length}
itemSize={100}
renderItem={renderItem}
/>
);
};
// Debounced dispatch pattern
const useDebounceDispatch = (delay: number = 300) => {
const dispatch = useAppDispatch();
return useMemo(
() => debounce(dispatch, delay),
[dispatch, delay]
);
};
const SearchInput = () => {
const debouncedDispatch = useDebounceDispatch();
const handleSearch = (query: string) => {
debouncedDispatch(searchAction(query));
};
return (
<input
onChange={(e) => handleSearch(e.target.value)}
placeholder="Search..."
/>
);
};
// Batch updates for multiple actions
const useBatchedDispatch = () => {
const dispatch = useAppDispatch();
return useCallback((actions: AnyAction[]) => {
unstable_batchedUpdates(() => {
actions.forEach(action => dispatch(action));
});
}, [dispatch]);
};
const BatchedActionsComponent = () => {
const batchedDispatch = useBatchedDispatch();
const handleBulkUpdate = () => {
batchedDispatch([
updateUser({ id: '1', name: 'Alice' }),
updateUser({ id: '2', name: 'Bob' }),
updateUser({ id: '3', name: 'Charlie' }),
refreshUsersList()
]);
};
return (
<button onClick={handleBulkUpdate}>
Bulk Update Users
</button>
);
};// RTK Query with React Suspense
const SuspenseQueryComponent = ({ postId }: { postId: string }) => {
const { data } = useGetPostQuery(postId, {
// Enable Suspense mode
suspense: true
});
// data is guaranteed to be defined in Suspense mode
return (
<article>
<h1>{data.title}</h1>
<p>{data.content}</p>
</article>
);
};
const AppWithSuspense = () => {
return (
<Suspense fallback={<div>Loading post...</div>}>
<SuspenseQueryComponent postId="123" />
</Suspense>
);
};// Error boundary for RTK Query errors
class RTKQueryErrorBoundary extends React.Component {
state = { hasError: false, error: null };
static getDerivedStateFromError(error: any) {
return { hasError: true, error };
}
componentDidCatch(error: any, errorInfo: any) {
// Log RTK Query errors
console.error('RTK Query Error:', error, errorInfo);
}
render() {
if (this.state.hasError) {
return <div>Something went wrong with data fetching.</div>;
}
return this.props.children;
}
}
const AppWithErrorBoundary = () => {
return (
<RTKQueryErrorBoundary>
<DataFetchingComponent />
</RTKQueryErrorBoundary>
);
};// SSR with RTK Query
export async function getServerSideProps() {
const store = configureStore({
reducer: { api: api.reducer },
middleware: (getDefaultMiddleware) =>
getDefaultMiddleware().concat(api.middleware)
});
// Prefetch data on server
store.dispatch(api.endpoints.getPosts.initiate());
await Promise.all(api.util.getRunningOperationPromises());
return {
props: {
initialReduxState: store.getState()
}
};
}Install with Tessl CLI
npx tessl i tessl/npm-reduxjs--toolkit