or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

actions.mdindex.mdmiddleware.mdreducer-composition.mdstore-management.mdutilities.md

middleware.mddocs/

0

# Middleware and Enhancement

1

2

Store enhancement system for adding middleware and extending store capabilities. Middleware enables async action handling, logging, routing, and other cross-cutting concerns by intercepting and potentially transforming actions before they reach reducers.

3

4

## Capabilities

5

6

### Apply Middleware

7

8

Creates a store enhancer that applies middleware to the dispatch method of the Redux store.

9

10

```typescript { .api }

11

/**

12

* Creates a store enhancer that applies middleware to the dispatch method

13

* @param middlewares - The middleware chain to be applied

14

* @returns A store enhancer applying the middleware

15

*/

16

function applyMiddleware(): StoreEnhancer;

17

18

function applyMiddleware<Ext1, S>(

19

middleware1: Middleware<Ext1, S, any>

20

): StoreEnhancer<{ dispatch: Ext1 }>;

21

22

function applyMiddleware<Ext1, Ext2, S>(

23

middleware1: Middleware<Ext1, S, any>,

24

middleware2: Middleware<Ext2, S, any>

25

): StoreEnhancer<{ dispatch: Ext1 & Ext2 }>;

26

27

function applyMiddleware<Ext1, Ext2, Ext3, S>(

28

middleware1: Middleware<Ext1, S, any>,

29

middleware2: Middleware<Ext2, S, any>,

30

middleware3: Middleware<Ext3, S, any>

31

): StoreEnhancer<{ dispatch: Ext1 & Ext2 & Ext3 }>;

32

33

function applyMiddleware<Ext1, Ext2, Ext3, Ext4, S>(

34

middleware1: Middleware<Ext1, S, any>,

35

middleware2: Middleware<Ext2, S, any>,

36

middleware3: Middleware<Ext3, S, any>,

37

middleware4: Middleware<Ext4, S, any>

38

): StoreEnhancer<{ dispatch: Ext1 & Ext2 & Ext3 & Ext4 }>;

39

40

function applyMiddleware<Ext1, Ext2, Ext3, Ext4, Ext5, S>(

41

middleware1: Middleware<Ext1, S, any>,

42

middleware2: Middleware<Ext2, S, any>,

43

middleware3: Middleware<Ext3, S, any>,

44

middleware4: Middleware<Ext4, S, any>,

45

middleware5: Middleware<Ext5, S, any>

46

): StoreEnhancer<{ dispatch: Ext1 & Ext2 & Ext3 & Ext4 & Ext5 }>;

47

48

function applyMiddleware<Ext, S = any>(

49

...middlewares: Middleware<any, S, any>[]

50

): StoreEnhancer<{ dispatch: Ext }>;

51

```

52

53

**Usage Examples:**

54

55

```typescript

56

import { applyMiddleware, legacy_createStore as createStore } from "redux";

57

import thunk from "redux-thunk";

58

import logger from "redux-logger";

59

60

// Single middleware

61

const store = createStore(rootReducer, applyMiddleware(thunk));

62

63

// Multiple middleware (order matters - applied left to right)

64

const store = createStore(

65

rootReducer,

66

applyMiddleware(thunk, logger)

67

);

68

69

// With preloaded state

70

const store = createStore(

71

rootReducer,

72

preloadedState,

73

applyMiddleware(thunk, logger)

74

);

75

76

// Conditional middleware based on environment

77

const middlewares = [thunk];

78

if (process.env.NODE_ENV === "development") {

79

middlewares.push(logger);

80

}

81

const store = createStore(rootReducer, applyMiddleware(...middlewares));

82

```

83

84

### Function Composition

85

86

Composes single-argument functions from right to left for combining store enhancers and middleware.

87

88

```typescript { .api }

89

/**

90

* Composes single-argument functions from right to left

91

* @param funcs - The functions to compose

92

* @returns A function obtained by composing the argument functions from right to left

93

*/

94

function compose(): <R>(a: R) => R;

95

96

function compose<F extends Function>(f: F): F;

97

98

function compose<A, T extends any[], R>(

99

f1: (a: A) => R,

100

f2: Func<T, A>

101

): Func<T, R>;

102

103

function compose<A, B, T extends any[], R>(

104

f1: (b: B) => R,

105

f2: (a: A) => B,

106

f3: Func<T, A>

107

): Func<T, R>;

108

109

function compose<A, B, C, T extends any[], R>(

110

f1: (c: C) => R,

111

f2: (b: B) => C,

112

f3: (a: A) => B,

113

f4: Func<T, A>

114

): Func<T, R>;

115

116

function compose<R>(

117

f1: (a: any) => R,

118

...funcs: Function[]

119

): (...args: any[]) => R;

120

121

function compose<R>(...funcs: Function[]): (...args: any[]) => R;

122

123

type Func<T extends any[], R> = (...a: T) => R;

124

```

125

126

**Usage Examples:**

127

128

```typescript

129

import { compose, applyMiddleware } from "redux";

130

131

// Compose multiple enhancers

132

const enhancer = compose(

133

applyMiddleware(thunk, logger),

134

window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__()

135

);

136

137

const store = createStore(rootReducer, enhancer);

138

139

// Compose functions (general use)

140

const addOne = (x: number) => x + 1;

141

const multiplyByTwo = (x: number) => x * 2;

142

const addThenMultiply = compose(multiplyByTwo, addOne);

143

144

console.log(addThenMultiply(3)); // (3 + 1) * 2 = 8

145

146

// Compose multiple transformation functions

147

const trim = (str: string) => str.trim();

148

const toLowerCase = (str: string) => str.toLowerCase();

149

const removeSpaces = (str: string) => str.replace(/\s+/g, "");

150

151

const normalizeString = compose(removeSpaces, toLowerCase, trim);

152

console.log(normalizeString(" Hello World ")); // "helloworld"

153

```

154

155

### Middleware Interface

156

157

The interface that Redux middleware must implement.

158

159

```typescript { .api }

160

/**

161

* A middleware is a higher-order function that composes a dispatch function

162

* to return a new dispatch function

163

* @template DispatchExt - Extra Dispatch signature added by this middleware

164

* @template S - The type of the state supported by this middleware

165

* @template D - The type of Dispatch of the store where this middleware is installed

166

*/

167

interface Middleware<

168

_DispatchExt = {},

169

S = any,

170

D extends Dispatch = Dispatch

171

> {

172

(api: MiddlewareAPI<D, S>): (

173

next: (action: unknown) => unknown

174

) => (action: unknown) => unknown;

175

}

176

177

/**

178

* The API object passed to middleware providing access to dispatch and getState

179

* @template D - The dispatch function type

180

* @template S - The state type

181

*/

182

interface MiddlewareAPI<D extends Dispatch = Dispatch, S = any> {

183

dispatch: D;

184

getState(): S;

185

}

186

```

187

188

**Usage Examples:**

189

190

```typescript

191

// Logger middleware

192

const loggerMiddleware: Middleware = (store) => (next) => (action) => {

193

console.log("Dispatching:", action);

194

console.log("Previous state:", store.getState());

195

196

const result = next(action);

197

198

console.log("Next state:", store.getState());

199

return result;

200

};

201

202

// Async middleware (like redux-thunk)

203

const thunkMiddleware: Middleware = (store) => (next) => (action) => {

204

if (typeof action === "function") {

205

return action(store.dispatch, store.getState);

206

}

207

return next(action);

208

};

209

210

// Error handling middleware

211

const crashReporterMiddleware: Middleware = (store) => (next) => (action) => {

212

try {

213

return next(action);

214

} catch (err) {

215

console.error("Caught an exception!", err);

216

// Report to crash reporting service

217

throw err;

218

}

219

};

220

221

// Performance monitoring middleware

222

const performanceMiddleware: Middleware = (store) => (next) => (action) => {

223

const start = performance.now();

224

const result = next(action);

225

const end = performance.now();

226

227

console.log(`Action ${action.type} took ${end - start} milliseconds`);

228

return result;

229

};

230

```

231

232

## Advanced Middleware Patterns

233

234

### Conditional Middleware

235

236

Middleware that only processes certain types of actions:

237

238

```typescript

239

const apiMiddleware: Middleware = (store) => (next) => (action) => {

240

// Only handle actions with 'api' meta property

241

if (!action.meta || !action.meta.api) {

242

return next(action);

243

}

244

245

const { url, method, types } = action.meta.api;

246

const [requestType, successType, failureType] = types;

247

248

// Dispatch request action

249

next({ type: requestType });

250

251

// Make API call

252

return fetch(url, { method })

253

.then(response => response.json())

254

.then(data => next({ type: successType, payload: data }))

255

.catch(error => next({ type: failureType, payload: error.message }));

256

};

257

```

258

259

### Middleware with State Dependencies

260

261

Middleware that behaves differently based on current state:

262

263

```typescript

264

const authMiddleware: Middleware = (store) => (next) => (action) => {

265

const state = store.getState();

266

267

// Redirect to login if user is not authenticated

268

if (!state.auth.isAuthenticated && action.type !== "LOGIN") {

269

console.warn("User not authenticated, redirecting to login");

270

return next({ type: "REDIRECT_TO_LOGIN" });

271

}

272

273

return next(action);

274

};

275

```

276

277

### Middleware Composition

278

279

Creating middleware that combines multiple behaviors:

280

281

```typescript

282

const createCompositeMiddleware = (...middlewares: Middleware[]) => {

283

return (store: MiddlewareAPI) => (next: Dispatch) => {

284

// Compose all middleware functions

285

const chain = middlewares.map(middleware => middleware(store));

286

return compose(...chain)(next);

287

};

288

};

289

290

// Use composite middleware

291

const compositeMiddleware = createCompositeMiddleware(

292

loggerMiddleware,

293

performanceMiddleware,

294

crashReporterMiddleware

295

);

296

```

297

298

### Type-Safe Middleware

299

300

Creating fully typed middleware with TypeScript:

301

302

```typescript

303

interface AppState {

304

counter: number;

305

user: User | null;

306

}

307

308

interface AppAction {

309

type: string;

310

payload?: any;

311

}

312

313

const typedMiddleware: Middleware<{}, AppState, Dispatch<AppAction>> =

314

(store) => (next) => (action) => {

315

// Full type safety for state and actions

316

const currentState: AppState = store.getState();

317

318

if (action.type === "INCREMENT" && currentState.counter >= 100) {

319

console.warn("Counter limit reached");

320

return next({ type: "COUNTER_LIMIT_REACHED" });

321

}

322

323

return next(action);

324

};

325

```

326

327

## Store Enhancement

328

329

### Store Enhancer Type

330

331

The type definition for store enhancers.

332

333

```typescript { .api }

334

/**

335

* A store enhancer is a higher-order function that composes a store creator

336

* to return a new, enhanced store creator

337

* @template Ext - Store extension that is mixed into the Store type

338

* @template StateExt - State extension that is mixed into the state type

339

*/

340

type StoreEnhancer<Ext extends {} = {}, StateExt extends {} = {}> = <

341

NextExt extends {},

342

NextStateExt extends {}

343

>(

344

next: StoreEnhancerStoreCreator<NextExt, NextStateExt>

345

) => StoreEnhancerStoreCreator<NextExt & Ext, NextStateExt & StateExt>;

346

347

type StoreEnhancerStoreCreator<

348

Ext extends {} = {},

349

StateExt extends {} = {}

350

> = <S, A extends Action, PreloadedState>(

351

reducer: Reducer<S, A, PreloadedState>,

352

preloadedState?: PreloadedState | undefined

353

) => Store<S, A, StateExt> & Ext;

354

```

355

356

### Custom Store Enhancers

357

358

Creating custom store enhancers for advanced functionality:

359

360

```typescript

361

// DevTools enhancer

362

const devToolsEnhancer: StoreEnhancer = (createStore) => (reducer, preloadedState) => {

363

const store = createStore(reducer, preloadedState);

364

365

if (window.__REDUX_DEVTOOLS_EXTENSION__) {

366

return {

367

...store,

368

// Add devtools capabilities

369

};

370

}

371

372

return store;

373

};

374

375

// Persistence enhancer

376

const persistenceEnhancer: StoreEnhancer = (createStore) => (reducer, preloadedState) => {

377

// Load persisted state

378

const persistedState = localStorage.getItem("redux-state");

379

const initialState = persistedState ? JSON.parse(persistedState) : preloadedState;

380

381

const store = createStore(reducer, initialState);

382

383

// Save state on every change

384

store.subscribe(() => {

385

localStorage.setItem("redux-state", JSON.stringify(store.getState()));

386

});

387

388

return store;

389

};

390

391

// Combine enhancers

392

const rootEnhancer = compose(

393

applyMiddleware(thunk, logger),

394

devToolsEnhancer,

395

persistenceEnhancer

396

);

397

```