or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

actions-reducers.mdasync-thunks.mdcore-store.mdentity-adapters.mdindex.mdmiddleware.mdreact-integration.mdrtk-query-react.mdrtk-query.mdutilities.md

react-integration.mddocs/

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

```