Skip to content

Commit 55253e8

Browse files
authored
allow configuration of createSerializableStateInvariantMiddleware ignoredActionPaths, update default value, add tests (#457)
1 parent 828026d commit 55253e8

File tree

4 files changed

+78
-25
lines changed

4 files changed

+78
-25
lines changed

etc/redux-toolkit.api.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -271,6 +271,7 @@ export { Selector }
271271
// @public
272272
export interface SerializableStateInvariantMiddlewareOptions {
273273
getEntries?: (value: any) => [string, any][];
274+
ignoredActionPaths?: string[];
274275
ignoredActions?: string[];
275276
ignoredPaths?: string[];
276277
isSerializable?: (value: any) => boolean;

src/createAsyncThunk.test.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -463,3 +463,16 @@ describe('createAsyncThunk with abortController', () => {
463463
})
464464
})
465465
})
466+
467+
test('non-serializable arguments are ignored by serializableStateInvariantMiddleware', async () => {
468+
const restore = mockConsole(createConsole())
469+
const nonSerializableValue = new Map()
470+
const asyncThunk = createAsyncThunk('test', (arg: Map<any, any>) => {})
471+
472+
configureStore({
473+
reducer: () => 0
474+
}).dispatch(asyncThunk(nonSerializableValue))
475+
476+
expect(getLog().log).toMatchInlineSnapshot(`""`)
477+
restore()
478+
})

src/serializableStateInvariantMiddleware.test.ts

Lines changed: 55 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -330,6 +330,61 @@ describe('serializableStateInvariantMiddleware', () => {
330330
expect(numTimesCalled).toBeGreaterThan(0)
331331
})
332332

333+
describe('ignored action paths', () => {
334+
function reducer() {
335+
return 0
336+
}
337+
const nonSerializableValue = new Map()
338+
339+
it('default value: meta.arg', () => {
340+
configureStore({
341+
reducer,
342+
middleware: [createSerializableStateInvariantMiddleware()]
343+
}).dispatch({ type: 'test', meta: { arg: nonSerializableValue } })
344+
345+
expect(getLog().log).toMatchInlineSnapshot(`""`)
346+
})
347+
348+
it('default value can be overridden', () => {
349+
configureStore({
350+
reducer,
351+
middleware: [
352+
createSerializableStateInvariantMiddleware({
353+
ignoredActionPaths: []
354+
})
355+
]
356+
}).dispatch({ type: 'test', meta: { arg: nonSerializableValue } })
357+
358+
expect(getLog().log).toMatchInlineSnapshot(`
359+
"A non-serializable value was detected in an action, in the path: \`meta.arg\`. Value: Map {}
360+
Take a look at the logic that dispatched this action: Object {
361+
\\"meta\\": Object {
362+
\\"arg\\": Map {},
363+
},
364+
\\"type\\": \\"test\\",
365+
}
366+
(See https://redux.js.org/faq/actions#why-should-type-be-a-string-or-at-least-serializable-why-should-my-action-types-be-constants)"
367+
`)
368+
})
369+
370+
it('can specify (multiple) different values', () => {
371+
configureStore({
372+
reducer,
373+
middleware: [
374+
createSerializableStateInvariantMiddleware({
375+
ignoredActionPaths: ['payload', 'meta.arg']
376+
})
377+
]
378+
}).dispatch({
379+
type: 'test',
380+
payload: { arg: nonSerializableValue },
381+
meta: { arg: nonSerializableValue }
382+
})
383+
384+
expect(getLog().log).toMatchInlineSnapshot(`""`)
385+
})
386+
})
387+
333388
it('should not check serializability for ignored slice names', () => {
334389
const ACTION_TYPE = 'TEST_ACTION'
335390

@@ -386,29 +441,6 @@ describe('serializableStateInvariantMiddleware', () => {
386441
`)
387442
})
388443

389-
it('should not check serializability for meta.args by default', () => {
390-
const badValue = new Map()
391-
392-
const reducer: Reducer = (state = 42, action) => {
393-
return state
394-
}
395-
396-
const serializableStateInvariantMiddleware = createSerializableStateInvariantMiddleware()
397-
398-
const store = configureStore({
399-
reducer: {
400-
testSlice: reducer
401-
},
402-
middleware: [serializableStateInvariantMiddleware]
403-
})
404-
405-
store.dispatch({ type: 'testAction', meta: { args: { badValue } } })
406-
407-
const { log } = getLog()
408-
expect(log).toBe('')
409-
const q = 42
410-
})
411-
412444
it('Should print a warning if execution takes too long', () => {
413445
const reducer: Reducer = (state = 42, action) => {
414446
return state

src/serializableStateInvariantMiddleware.ts

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ export function findNonSerializableValue(
3636
path: ReadonlyArray<string> = [],
3737
isSerializable: (value: unknown) => boolean = isPlain,
3838
getEntries?: (value: unknown) => [string, any][],
39-
ignoredPaths: string[] = ['meta.args']
39+
ignoredPaths: string[] = []
4040
): NonSerializableValue | false {
4141
let foundNestedSerializable: NonSerializableValue | false
4242

@@ -111,6 +111,11 @@ export interface SerializableStateInvariantMiddlewareOptions {
111111
*/
112112
ignoredActions?: string[]
113113

114+
/**
115+
* An array of dot-separated path strings to ignore when checking for serializability, Defaults to ['meta.arg']
116+
*/
117+
ignoredActionPaths?: string[]
118+
114119
/**
115120
* An array of dot-separated path strings to ignore when checking for serializability, Defaults to []
116121
*/
@@ -140,6 +145,7 @@ export function createSerializableStateInvariantMiddleware(
140145
isSerializable = isPlain,
141146
getEntries,
142147
ignoredActions = [],
148+
ignoredActionPaths = ['meta.arg'],
143149
ignoredPaths = [],
144150
warnAfter = 32
145151
} = options
@@ -158,7 +164,8 @@ export function createSerializableStateInvariantMiddleware(
158164
action,
159165
[],
160166
isSerializable,
161-
getEntries
167+
getEntries,
168+
ignoredActionPaths
162169
)
163170

164171
if (foundActionNonSerializableValue) {

0 commit comments

Comments
 (0)