Skip to content

Commit f86f974

Browse files
authored
Update docs on serializability usage and dev check middleware (#630)
* Add more serializability usage info * Update Docusaurus to alpha.58 * Remove unneeded pagination CSS * Split API ref into subcategories * Extract API ref docs for dev check middleware * Update API types * Assorted typo fixes and cleanup
1 parent 05bdfbb commit f86f974

17 files changed

+3500
-3834
lines changed

docs/api/configureStore.md

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,11 @@ type ConfigureEnhancersCallback = (
1919
defaultEnhancers: StoreEnhancer[]
2020
) => StoreEnhancer[]
2121

22-
interface ConfigureStoreOptions<S = any, A extends Action = AnyAction> {
22+
interface ConfigureStoreOptions<
23+
S = any,
24+
A extends Action = AnyAction,
25+
M extends Middlewares<S> = Middlewares<S>
26+
> {
2327
/**
2428
* A single reducer function that will be used as the root reducer, or an
2529
* object of slice reducers that will be passed to `combineReducers()`.
@@ -30,7 +34,7 @@ interface ConfigureStoreOptions<S = any, A extends Action = AnyAction> {
3034
* An array of Redux middleware to install. If not supplied, defaults to
3135
* the set of middleware returned by `getDefaultMiddleware()`.
3236
*/
33-
middleware?: Middleware<{}, S>[]
37+
middleware?: ((getDefaultMiddleware: CurriedGetDefaultMiddleware<S>) => M) | M
3438

3539
/**
3640
* Whether to enable Redux DevTools integration. Defaults to `true`.
@@ -55,7 +59,7 @@ interface ConfigureStoreOptions<S = any, A extends Action = AnyAction> {
5559
* If you need to customize the order of enhancers, supply a callback
5660
* function that will receive the original array (ie, `[applyMiddleware]`),
5761
* and should return a new array (such as `[applyMiddleware, offline]`).
58-
* If you only need to add middleware, use the `middleware` parameter instead.
62+
* If you only need to add middleware, you can use the `middleware` parameter instead.
5963
*/
6064
enhancers?: StoreEnhancer[] | ConfigureEnhancersCallback
6165
}
@@ -75,14 +79,18 @@ If it is an object of slice reducers, like `{users : usersReducer, posts : posts
7579

7680
### `middleware`
7781

78-
An optional array of Redux middleware functions.
82+
An optional array of Redux middleware functions
7983

8084
If this option is provided, it should contain all the middleware functions you
8185
want added to the store. `configureStore` will automatically pass those to `applyMiddleware`.
8286

8387
If not provided, `configureStore` will call `getDefaultMiddleware` and use the
8488
array of middleware functions it returns.
8589

90+
Alternately, you may pass a callback function that will receive `getDefaultMiddleware` as its argument,
91+
and should return a middleware array. This lets you skip importing `getDefaultMiddleware` separately. If using TypeScript, prefer using this syntax, as we provide a more strongly-typed version of `getDefaultMiddleware` that will correctly
92+
retain the types of the provided middleware when constructing the store.
93+
8694
For more details on how the `middleware` parameter works and the list of middleware that are added by default, see the
8795
[`getDefaultMiddleware` docs page](./getDefaultMiddleware.md).
8896

@@ -181,5 +189,5 @@ const store = configureStore({
181189
// - The slice reducers were automatically passed to combineReducers()
182190
// - redux-thunk and redux-logger were added as middleware
183191
// - The Redux DevTools Extension is disabled for production
184-
// - The middleware, batch, and devtools enhancers were automatically composed together
192+
// - The middleware, batch, and devtools enhancers were composed together
185193
```

docs/api/createReducer.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,7 @@ const exampleReducer = createReducer(initialState, builder => {
8686
})
8787
```
8888

89-
See [the `builder callback` API](#the-builder-callback-api) below for details on defining reducers using this syntax.
89+
See the ["Builder Callback Notation"](#builder-callback-notation) section below for details on defining reducers using this syntax.
9090

9191
> **Note**: If you are using TypeScript, we specifically recommend using the builder callback API to get proper inference of TS types for action objects. If you do not use the builder callback and are using TypeScript, you will need to use `actionCreator.type` or `actionCreator.toString()` as the key to force the TS compiler to accept the computed property. Please see [Usage With TypeScript](./../usage/usage-with-typescript.md#type-safety-with-extraReducers) for further details.
9292
@@ -167,7 +167,7 @@ const todosReducer = createReducer([], {
167167
})
168168
```
169169

170-
## The "builder callback" API
170+
## Builder Callback Notation
171171

172172
Instead of using a plain object as an argument to `createReducer`, you can also provide a "builder callback" function that receives an `ActionReducerMapBuilder` instance:
173173

@@ -319,7 +319,7 @@ console.log(reducer(0, { type: 'increment' }))
319319
// - matcher ends with 't': 5 => 7
320320
```
321321

322-
## Debugging your state
322+
## Logging Draft State Values
323323

324324
It's very common for a developer to call `console.log(state)` during the development process. However, browsers display Proxies in a format that is hard to read, which can make console logging of Immer-based state difficult.
325325

docs/api/createSlice.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -154,7 +154,7 @@ createSlice({
154154

155155
We recommend using this API if stricter type safety is necessary when defining reducer argument objects, as it will correctly infer the action type in the reducer based on the provided action creator. It's particularly useful for working with actions produced by `createAction` and `createAsyncThunk`.
156156

157-
See [the "builder callback API" section of the `createReducer` reference](./createReducer.md#the-builder-callback-api) for details on how to use `builder.addCase`, `builder.addMatcher`, and `builder.addDefault`
157+
See [the "Builder Callback Notation" section of the `createReducer` reference](./createReducer.md#builder-callback-notation) for details on how to use `builder.addCase`, `builder.addMatcher`, and `builder.addDefault`
158158

159159
## Return Value
160160

docs/api/getDefaultMiddleware.md

Lines changed: 7 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ const store = configureStore({
1818
reducer: rootReducer
1919
})
2020

21-
// Store has one or more middleware added, because the middleware list was not customized
21+
// Store has middleware added, because the middleware list was not customized
2222
```
2323

2424
If you want to customize the list of middleware, you can supply an array of middleware functions to `configureStore`:
@@ -47,7 +47,7 @@ const store = configureStore({
4747
// Store has all of the default middleware added, _plus_ the logger middleware
4848
```
4949

50-
## Usage without import (middleware callback notation)
50+
## Middleware Callback Notation for `configureStore`
5151

5252
For convenience, the `middleware` property of `configureStore` can be used with a callback notation like this.
5353

@@ -72,12 +72,12 @@ One of the goals of Redux Toolkit is to provide opinionated defaults and prevent
7272
`getDefaultMiddleware` includes some middleware that are added **in development builds of your app only** to
7373
provide runtime checks for two common issues:
7474

75-
- [`immutable-state-invariant`](./otherExports.md#createimmutablestateinvariantmiddleware): deeply compares
75+
- [Immutability check middleware](./immutabilityMiddleware.md): deeply compares
7676
state values for mutations. It can detect mutations in reducers during a dispatch, and also mutations that occur between
7777
dispatches (such as in a component or a selector). When a mutation is detected, it will throw an error and indicate the key
7878
path for where the mutated value was detected in the state tree. (Forked from [`redux-immutable-state-invariant`](https://github.com/leoasis/redux-immutable-state-invariant).)
7979

80-
- [`serializable-state-invariant-middleware`](./otherExports.md#createserializablestateinvariantmiddleware): a custom middleware created specifically for use in Redux Toolkit. Similar in
80+
- [Serializability check middleware](./serializabilityMiddleware.md): a custom middleware created specifically for use in Redux Toolkit. Similar in
8181
concept to `immutable-state-invariant`, but deeply checks your state tree and your actions for non-serializable values
8282
such as functions, Promises, Symbols, and other non-plain-JS-data values. When a non-serializable value is detected, a
8383
console error will be printed with the key path for where the non-serializable value was detected.
@@ -103,7 +103,7 @@ const middleware = [thunk]
103103

104104
`getDefaultMiddleware` accepts an options object that allows customizing each middleware in two ways:
105105

106-
- Each middleware can be excluded from inclusion in the array by passing `false` for its corresponding field
106+
- Each middleware can be excluded the result array by passing `false` for its corresponding field
107107
- Each middleware can have its options customized by passing the matching options object for its corresponding field
108108

109109
This example shows excluding the serializable state check middleware, and passing a specific value for the thunk
@@ -126,46 +126,11 @@ interface ThunkOptions<E = any> {
126126
}
127127

128128
interface ImmutableStateInvariantMiddlewareOptions {
129-
isImmutable?: (value: any) => boolean
130-
ignoredPaths?: string[]
131-
warnAfter?: number
129+
// See "Immutability Middleware" page for definition
132130
}
133131

134132
interface SerializableStateInvariantMiddlewareOptions {
135-
/**
136-
* The function to check if a value is considered serializable. This
137-
* function is applied recursively to every value contained in the
138-
* state. Defaults to `isPlain()`.
139-
*/
140-
isSerializable?: (value: any) => boolean
141-
/**
142-
* The function that will be used to retrieve entries from each
143-
* value. If unspecified, `Object.entries` will be used. Defaults
144-
* to `undefined`.
145-
*/
146-
getEntries?: (value: any) => [string, any][]
147-
148-
/**
149-
* An array of action types to ignore when checking for serializability, Defaults to []
150-
*/
151-
ignoredActions?: string[]
152-
153-
/**
154-
* An array of dot-separated path strings to ignore when checking for serializability, Defaults to ['meta.arg']
155-
* If you use this parameter, the default value 'meta.arg' will be removed, so we recommend re-adding it unless you
156-
* specifically do not want to ignore it. Example: ['meta.arg', 'your.path', 'other.path', ...etc]
157-
*/
158-
ignoredActionPaths?: string[]
159-
160-
/**
161-
* An array of dot-separated path strings to ignore when checking for serializability, Defaults to []
162-
*/
163-
ignoredPaths?: string[]
164-
165-
/**
166-
* Execution time warning threshold. If the middleware takes longer than `warnAfter` ms, a warning will be displayed in the console. Defaults to 32
167-
*/
168-
warnAfter?: number
133+
// See "Serializability Middleware" page for definition
169134
}
170135

171136
interface GetDefaultMiddlewareOptions {

docs/api/immutabilityMiddleware.md

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
---
2+
id: immutabilityMiddleware
3+
title: Immutability Middleware
4+
sidebar_label: Immutability Middleware
5+
hide_title: true
6+
---
7+
8+
# Immutability Middleware
9+
10+
A port of the [`redux-immutable-state-invariant`](https://github.com/leoasis/redux-immutable-state-invariant) middleware, customized for use with Redux Toolkit. Any detected mutations will be thrown as errors.
11+
12+
This middleware is added to the store by default by [`configureStore`](./configureStore.md) and [`getDefaultMiddleware`](./getDefaultMiddleware.md).
13+
14+
You can customize the behavior of this middleware by passing any of the supported options as the `immutableCheck` value for `getDefaultMiddleware`.
15+
16+
## Options
17+
18+
```ts
19+
type IsImmutableFunc = (value: any) => boolean
20+
21+
interface ImmutableStateInvariantMiddlewareOptions {
22+
/**
23+
Callback function to check if a value is considered to be immutable.
24+
This function is applied recursively to every value contained in the state.
25+
The default implementation will return true for primitive types
26+
(like numbers, strings, booleans, null and undefined).
27+
*/
28+
isImmutable?: IsImmutableFunc
29+
/**
30+
An array of dot-separated path strings that match named nodes from
31+
the root state to ignore when checking for immutability.
32+
Defaults to undefined
33+
*/
34+
ignoredPaths?: string[]
35+
/** Print a warning if checks take longer than N ms. Default: 32ms */
36+
warnAfter?: number
37+
// @deprecated. Use ignoredPaths
38+
ignore?: string[]
39+
}
40+
```
41+
42+
## Exports
43+
44+
### `createImmutableStateInvariantMiddleware`
45+
46+
Creates an instance of the immutability check middleware, with the given options.
47+
48+
You will most likely not need to call this yourself, as `getDefaultMiddleware` already does so.
49+
50+
Example:
51+
52+
```js
53+
import {
54+
createSlice,
55+
configureStore,
56+
createImmutableStateInvariantMiddleware
57+
} from '@reduxjs/toolkit'
58+
59+
const exampleSlice = createSlice({
60+
name: 'example',
61+
initialState: {
62+
user: 'will track changes',
63+
ignoredPath: 'single level',
64+
ignoredNested: {
65+
one: 'one',
66+
two: 'two'
67+
}
68+
},
69+
reducers: {}
70+
})
71+
72+
const immutableInvariantMiddleware = createImmutableStateInvariantMiddleware({
73+
ignoredPaths: ['ignoredPath', 'ignoredNested.one', 'ignoredNested.two']
74+
})
75+
76+
const store = configureStore({
77+
reducer: exampleSlice.reducer,
78+
// Note that this will replace all default middleware
79+
middleware: [immutableInvariantMiddleware]
80+
})
81+
```
82+
83+
### `isImmutableDefault`
84+
85+
Default implementation of the "is this value immutable?" check. Currently implemented as:
86+
87+
```js
88+
return (
89+
typeof value !== 'object' || value === null || typeof value === 'undefined'
90+
)
91+
```
92+
93+
This will return true for primitive types (like numbers, strings, booleans, null and undefined)

0 commit comments

Comments
 (0)