0
# React Integration
1
2
Redux Toolkit provides React-specific enhancements and utilities available from `@reduxjs/toolkit/react` that extend the core functionality with React-optimized features.
3
4
## Capabilities
5
6
### Enhanced Dynamic Middleware
7
8
React-enhanced version of dynamic middleware with component-specific hook factories and context integration.
9
10
```typescript { .api }
11
/**
12
* React-enhanced dynamic middleware with hook factories
13
* Available from @reduxjs/toolkit/react
14
*/
15
function createDynamicMiddleware<
16
State = any,
17
DispatchType extends Dispatch<AnyAction> = Dispatch<AnyAction>
18
>(): ReactDynamicMiddlewareInstance<State, DispatchType>;
19
20
interface ReactDynamicMiddlewareInstance<State, DispatchType> extends DynamicMiddlewareInstance<State, DispatchType> {
21
/** Creates hook factory for specific React context */
22
createDispatchWithMiddlewareHookFactory(context?: React.Context<any>): CreateDispatchWithMiddlewareHook<State, DispatchType>;
23
24
/** Default hook factory for current React context */
25
createDispatchWithMiddlewareHook: CreateDispatchWithMiddlewareHook<State, DispatchType>;
26
}
27
28
interface CreateDispatchWithMiddlewareHook<State, DispatchType> {
29
/** Create dispatch function with additional middleware */
30
(middleware: Middleware<any, State, DispatchType>[]): DispatchType;
31
}
32
```
33
34
**Usage Examples:**
35
36
```typescript
37
import { createDynamicMiddleware } from '@reduxjs/toolkit/react';
38
import { Provider } from 'react-redux';
39
40
// Create React-enhanced dynamic middleware
41
const dynamicMiddleware = createDynamicMiddleware<RootState, AppDispatch>();
42
43
const store = configureStore({
44
reducer: rootReducer,
45
middleware: (getDefaultMiddleware) =>
46
getDefaultMiddleware().concat(dynamicMiddleware.middleware)
47
});
48
49
// Component-specific middleware
50
const AnalyticsComponent = () => {
51
const dispatch = dynamicMiddleware.createDispatchWithMiddlewareHook([
52
analyticsMiddleware,
53
performanceMiddleware
54
]);
55
56
const handleAction = () => {
57
// This dispatch goes through analytics and performance middleware
58
dispatch(trackableAction('button_click'));
59
};
60
61
return <button onClick={handleAction}>Track This Click</button>;
62
};
63
64
// Feature-specific middleware
65
const FeatureComponent = () => {
66
const dispatchWithFeatureMiddleware = dynamicMiddleware.createDispatchWithMiddlewareHook([
67
featureLoggingMiddleware,
68
featureValidationMiddleware
69
]);
70
71
const handleFeatureAction = () => {
72
dispatchWithFeatureMiddleware(featureAction({ data: 'test' }));
73
};
74
75
return <button onClick={handleFeatureAction}>Feature Action</button>;
76
};
77
78
// Context-specific middleware factory
79
const CustomContext = React.createContext<any>(null);
80
81
const ContextualComponent = () => {
82
const createContextualDispatch = dynamicMiddleware.createDispatchWithMiddlewareHookFactory(CustomContext);
83
const dispatch = createContextualDispatch([contextSpecificMiddleware]);
84
85
return (
86
<CustomContext.Provider value={/* contextual data */}>
87
<button onClick={() => dispatch(contextAction())}>
88
Contextual Action
89
</button>
90
</CustomContext.Provider>
91
);
92
};
93
```
94
95
### React Hook Patterns
96
97
Advanced patterns for integrating Redux Toolkit with React components and hooks.
98
99
```typescript { .api }
100
/**
101
* Type-safe hooks for Redux Toolkit integration
102
*/
103
interface TypedReduxHooks<RootState, AppDispatch> {
104
useAppDispatch: () => AppDispatch;
105
useAppSelector: TypedUseSelectorHook<RootState>;
106
}
107
108
/**
109
* Custom hook factory for component-specific middleware
110
*/
111
type MiddlewareHookFactory<State, DispatchType> = (
112
middleware: Middleware<any, State, DispatchType>[]
113
) => DispatchType;
114
```
115
116
**Usage Examples:**
117
118
```typescript
119
import { TypedUseSelectorHook, useDispatch, useSelector } from 'react-redux';
120
import type { RootState, AppDispatch } from './store';
121
122
// Typed hooks setup
123
export const useAppDispatch = () => useDispatch<AppDispatch>();
124
export const useAppSelector: TypedUseSelectorHook<RootState> = useSelector;
125
126
// Component with typed hooks
127
const TypedComponent = () => {
128
const dispatch = useAppDispatch(); // Fully typed dispatch
129
const user = useAppSelector(state => state.auth.user); // Typed selector
130
131
const handleLogin = () => {
132
dispatch(loginUser({ username: 'test' })); // Type-checked action
133
};
134
135
return (
136
<div>
137
{user ? `Welcome ${user.name}` : 'Please log in'}
138
<button onClick={handleLogin}>Login</button>
139
</div>
140
);
141
};
142
143
// Custom hook for feature-specific middleware
144
const useFeatureDispatch = () => {
145
return dynamicMiddleware.createDispatchWithMiddlewareHook([
146
featureMiddleware,
147
debugMiddleware
148
]);
149
};
150
151
const FeatureComponent = () => {
152
const featureDispatch = useFeatureDispatch();
153
154
return (
155
<button onClick={() => featureDispatch(featureAction())}>
156
Feature Action with Custom Middleware
157
</button>
158
);
159
};
160
161
// Conditional middleware based on component props
162
const ConditionalMiddlewareComponent = ({ enableAnalytics }: { enableAnalytics: boolean }) => {
163
const middleware = useMemo(() => {
164
const middlewares: Middleware[] = [loggingMiddleware];
165
if (enableAnalytics) {
166
middlewares.push(analyticsMiddleware);
167
}
168
return middlewares;
169
}, [enableAnalytics]);
170
171
const dispatch = dynamicMiddleware.createDispatchWithMiddlewareHook(middleware);
172
173
return (
174
<button onClick={() => dispatch(trackedAction())}>
175
{enableAnalytics ? 'Tracked Action' : 'Regular Action'}
176
</button>
177
);
178
};
179
```
180
181
### Component Lifecycle Integration
182
183
Integrate Redux Toolkit with React component lifecycles for automatic cleanup and state management.
184
185
```typescript { .api }
186
/**
187
* Hook for component-scoped listeners
188
*/
189
interface ComponentListenerHook<StateType, DispatchType> {
190
(options: {
191
actionCreator?: any;
192
matcher?: ActionMatcher<any>;
193
effect: ListenerEffect<any, StateType, DispatchType, any>;
194
}): void;
195
}
196
```
197
198
**Usage Examples:**
199
200
```typescript
201
import { useEffect, useRef } from 'react';
202
import { createListenerMiddleware } from '@reduxjs/toolkit';
203
204
// Component-scoped listener middleware
205
const ComponentWithListeners = () => {
206
const listenerMiddleware = useRef(createListenerMiddleware()).current;
207
const dispatch = useAppDispatch();
208
209
useEffect(() => {
210
// Add component-specific listeners
211
const unsubscribe = listenerMiddleware.startListening({
212
actionCreator: componentAction,
213
effect: async (action, listenerApi) => {
214
// Component-specific side effects
215
console.log('Component action:', action.payload);
216
217
// Access component context through closure
218
// Perform component-related operations
219
}
220
});
221
222
// Cleanup on unmount
223
return () => {
224
unsubscribe();
225
listenerMiddleware.clearListeners();
226
};
227
}, [listenerMiddleware]);
228
229
const handleAction = () => {
230
dispatch(componentAction({ data: 'component data' }));
231
};
232
233
return <button onClick={handleAction}>Trigger Component Action</button>;
234
};
235
236
// HOC for automatic middleware injection
237
const withMiddleware = <P extends object>(
238
Component: React.ComponentType<P>,
239
middleware: Middleware[]
240
) => {
241
return (props: P) => {
242
const dispatch = dynamicMiddleware.createDispatchWithMiddlewareHook(middleware);
243
244
// Provide enhanced dispatch through context or props
245
return <Component {...props} dispatch={dispatch} />;
246
};
247
};
248
249
// Usage of HOC
250
const EnhancedComponent = withMiddleware(BaseComponent, [
251
analyticsMiddleware,
252
loggingMiddleware
253
]);
254
255
// Hook for automatic listener cleanup
256
const useComponentListener = (
257
actionCreator: any,
258
effect: (action: any) => void,
259
deps: React.DependencyList = []
260
) => {
261
const listenerRef = useRef<() => void>();
262
263
useEffect(() => {
264
// Remove previous listener
265
listenerRef.current?.();
266
267
// Add new listener
268
listenerRef.current = listenerMiddleware.startListening({
269
actionCreator,
270
effect: (action, listenerApi) => effect(action)
271
});
272
273
// Cleanup function
274
return () => {
275
listenerRef.current?.();
276
};
277
}, deps);
278
};
279
280
// Component using automatic listeners
281
const AutoListenerComponent = ({ userId }: { userId: string }) => {
282
useComponentListener(
283
userUpdated,
284
(action) => {
285
if (action.payload.id === userId) {
286
console.log('Current user updated:', action.payload);
287
}
288
},
289
[userId]
290
);
291
292
return <div>Component with automatic listeners</div>;
293
};
294
```
295
296
### Context-Aware Middleware
297
298
Middleware that can access React context values for enhanced functionality.
299
300
```typescript { .api }
301
/**
302
* Context-aware middleware factory
303
*/
304
interface ContextAwareMiddlewareFactory<ContextType> {
305
(context: ContextType): Middleware;
306
}
307
```
308
309
**Usage Examples:**
310
311
```typescript
312
// Theme-aware middleware
313
const ThemeContext = React.createContext<{ theme: 'light' | 'dark' }>({ theme: 'light' });
314
315
const createThemeAwareMiddleware = (theme: 'light' | 'dark'): Middleware =>
316
(store) => (next) => (action) => {
317
// Add theme information to actions
318
if (action.type.startsWith('ui/')) {
319
return next({
320
...action,
321
meta: {
322
...action.meta,
323
theme
324
}
325
});
326
}
327
return next(action);
328
};
329
330
const ThemedComponent = () => {
331
const { theme } = useContext(ThemeContext);
332
const dispatch = dynamicMiddleware.createDispatchWithMiddlewareHook([
333
createThemeAwareMiddleware(theme),
334
uiLoggingMiddleware
335
]);
336
337
return (
338
<button onClick={() => dispatch(uiAction('button_click'))}>
339
Themed Button
340
</button>
341
);
342
};
343
344
// User context-aware middleware
345
const UserContext = React.createContext<{ user: User | null }>({ user: null });
346
347
const createUserAwareMiddleware = (user: User | null): Middleware =>
348
(store) => (next) => (action) => {
349
// Add user context to all actions
350
const enhancedAction = {
351
...action,
352
meta: {
353
...action.meta,
354
userId: user?.id,
355
userRole: user?.role,
356
timestamp: Date.now()
357
}
358
};
359
360
return next(enhancedAction);
361
};
362
363
const UserAwareComponent = () => {
364
const { user } = useContext(UserContext);
365
const dispatch = dynamicMiddleware.createDispatchWithMiddlewareHook([
366
createUserAwareMiddleware(user),
367
auditMiddleware
368
]);
369
370
return (
371
<button onClick={() => dispatch(userAction('profile_update'))}>
372
Update Profile
373
</button>
374
);
375
};
376
377
// Multi-context middleware
378
const AppContexts = {
379
theme: ThemeContext,
380
user: UserContext,
381
feature: FeatureContext
382
};
383
384
const useContextAwareDispatch = () => {
385
const theme = useContext(ThemeContext);
386
const user = useContext(UserContext);
387
const features = useContext(FeatureContext);
388
389
return dynamicMiddleware.createDispatchWithMiddlewareHook([
390
createThemeAwareMiddleware(theme.theme),
391
createUserAwareMiddleware(user.user),
392
createFeatureAwareMiddleware(features.enabledFeatures)
393
]);
394
};
395
396
const MultiContextComponent = () => {
397
const dispatch = useContextAwareDispatch();
398
399
return (
400
<button onClick={() => dispatch(contextualAction())}>
401
Context-Aware Action
402
</button>
403
);
404
};
405
```
406
407
### Performance Optimization Patterns
408
409
React-specific patterns for optimizing Redux Toolkit performance in React applications.
410
411
```typescript { .api }
412
/**
413
* Optimized selector patterns
414
*/
415
interface OptimizedSelectorPatterns {
416
/** Memoized selector with shallow equality */
417
shallowEqual: (a: any, b: any) => boolean;
418
/** Selector with custom equality function */
419
customEqual: (equalityFn: (a: any, b: any) => boolean) => any;
420
}
421
```
422
423
**Usage Examples:**
424
425
```typescript
426
import { shallowEqual } from 'react-redux';
427
import { createSelector } from '@reduxjs/toolkit';
428
429
// Optimized selectors to prevent unnecessary re-renders
430
const selectOptimizedUserData = createSelector(
431
[(state: RootState) => state.user.profile],
432
(profile) => ({
433
name: profile?.name,
434
email: profile?.email,
435
avatar: profile?.avatar
436
})
437
);
438
439
const OptimizedUserComponent = () => {
440
// Use shallow equality to prevent re-renders when reference changes but values don't
441
const userData = useAppSelector(selectOptimizedUserData, shallowEqual);
442
443
return (
444
<div>
445
<img src={userData.avatar} alt={userData.name} />
446
<span>{userData.name}</span>
447
</div>
448
);
449
};
450
451
// Memoized component with Redux integration
452
const MemoizedPostItem = React.memo(({ postId }: { postId: string }) => {
453
const post = useAppSelector(
454
state => state.posts.entities[postId],
455
shallowEqual
456
);
457
458
if (!post) return null;
459
460
return (
461
<article>
462
<h3>{post.title}</h3>
463
<p>{post.content}</p>
464
</article>
465
);
466
});
467
468
// Virtualized list with Redux integration
469
const VirtualizedPostsList = () => {
470
const postIds = useAppSelector(state => state.posts.ids);
471
472
const renderItem = useCallback(({ index }: { index: number }) => {
473
return <MemoizedPostItem key={postIds[index]} postId={postIds[index]} />;
474
}, [postIds]);
475
476
return (
477
<VirtualList
478
height={400}
479
itemCount={postIds.length}
480
itemSize={100}
481
renderItem={renderItem}
482
/>
483
);
484
};
485
486
// Debounced dispatch pattern
487
const useDebounceDispatch = (delay: number = 300) => {
488
const dispatch = useAppDispatch();
489
490
return useMemo(
491
() => debounce(dispatch, delay),
492
[dispatch, delay]
493
);
494
};
495
496
const SearchInput = () => {
497
const debouncedDispatch = useDebounceDispatch();
498
499
const handleSearch = (query: string) => {
500
debouncedDispatch(searchAction(query));
501
};
502
503
return (
504
<input
505
onChange={(e) => handleSearch(e.target.value)}
506
placeholder="Search..."
507
/>
508
);
509
};
510
511
// Batch updates for multiple actions
512
const useBatchedDispatch = () => {
513
const dispatch = useAppDispatch();
514
515
return useCallback((actions: AnyAction[]) => {
516
unstable_batchedUpdates(() => {
517
actions.forEach(action => dispatch(action));
518
});
519
}, [dispatch]);
520
};
521
522
const BatchedActionsComponent = () => {
523
const batchedDispatch = useBatchedDispatch();
524
525
const handleBulkUpdate = () => {
526
batchedDispatch([
527
updateUser({ id: '1', name: 'Alice' }),
528
updateUser({ id: '2', name: 'Bob' }),
529
updateUser({ id: '3', name: 'Charlie' }),
530
refreshUsersList()
531
]);
532
};
533
534
return (
535
<button onClick={handleBulkUpdate}>
536
Bulk Update Users
537
</button>
538
);
539
};
540
```
541
542
## Advanced Integration Patterns
543
544
### Suspense Integration
545
546
```typescript
547
// RTK Query with React Suspense
548
const SuspenseQueryComponent = ({ postId }: { postId: string }) => {
549
const { data } = useGetPostQuery(postId, {
550
// Enable Suspense mode
551
suspense: true
552
});
553
554
// data is guaranteed to be defined in Suspense mode
555
return (
556
<article>
557
<h1>{data.title}</h1>
558
<p>{data.content}</p>
559
</article>
560
);
561
};
562
563
const AppWithSuspense = () => {
564
return (
565
<Suspense fallback={<div>Loading post...</div>}>
566
<SuspenseQueryComponent postId="123" />
567
</Suspense>
568
);
569
};
570
```
571
572
### Error Boundary Integration
573
574
```typescript
575
// Error boundary for RTK Query errors
576
class RTKQueryErrorBoundary extends React.Component {
577
state = { hasError: false, error: null };
578
579
static getDerivedStateFromError(error: any) {
580
return { hasError: true, error };
581
}
582
583
componentDidCatch(error: any, errorInfo: any) {
584
// Log RTK Query errors
585
console.error('RTK Query Error:', error, errorInfo);
586
}
587
588
render() {
589
if (this.state.hasError) {
590
return <div>Something went wrong with data fetching.</div>;
591
}
592
593
return this.props.children;
594
}
595
}
596
597
const AppWithErrorBoundary = () => {
598
return (
599
<RTKQueryErrorBoundary>
600
<DataFetchingComponent />
601
</RTKQueryErrorBoundary>
602
);
603
};
604
```
605
606
### Server-Side Rendering (SSR) Integration
607
608
```typescript
609
// SSR with RTK Query
610
export async function getServerSideProps() {
611
const store = configureStore({
612
reducer: { api: api.reducer },
613
middleware: (getDefaultMiddleware) =>
614
getDefaultMiddleware().concat(api.middleware)
615
});
616
617
// Prefetch data on server
618
store.dispatch(api.endpoints.getPosts.initiate());
619
620
await Promise.all(api.util.getRunningOperationPromises());
621
622
return {
623
props: {
624
initialReduxState: store.getState()
625
}
626
};
627
}
628
```