@depup/reduxjs__toolkit 2.12.0-depup.28 → 2.13.0-depup.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +4 -4
- package/changes.json +3 -3
- package/dist/cjs/redux-toolkit.development.cjs +1755 -2247
- package/dist/cjs/redux-toolkit.development.cjs.map +1 -1
- package/dist/cjs/redux-toolkit.production.min.cjs +1 -2
- package/dist/cjs/redux-toolkit.production.min.cjs.map +1 -1
- package/dist/index.d.mts +1496 -1525
- package/dist/index.d.ts +1496 -1525
- package/dist/query/cjs/rtk-query.development.cjs +2059 -2990
- package/dist/query/cjs/rtk-query.development.cjs.map +1 -1
- package/dist/query/cjs/rtk-query.production.min.cjs +1 -1
- package/dist/query/cjs/rtk-query.production.min.cjs.map +1 -1
- package/dist/query/index.d.mts +2300 -2308
- package/dist/query/index.d.ts +2300 -2308
- package/dist/query/react/cjs/rtk-query-react.development.cjs +609 -714
- package/dist/query/react/cjs/rtk-query-react.development.cjs.map +1 -1
- package/dist/query/react/cjs/rtk-query-react.production.min.cjs +1 -1
- package/dist/query/react/cjs/rtk-query-react.production.min.cjs.map +1 -1
- package/dist/query/react/index.d.mts +516 -523
- package/dist/query/react/index.d.ts +516 -523
- package/dist/query/react/rtk-query-react.browser.mjs +1 -1
- package/dist/query/react/rtk-query-react.browser.mjs.map +1 -1
- package/dist/query/react/rtk-query-react.legacy-esm.js +672 -714
- package/dist/query/react/rtk-query-react.legacy-esm.js.map +1 -1
- package/dist/query/react/rtk-query-react.modern.mjs +586 -679
- package/dist/query/react/rtk-query-react.modern.mjs.map +1 -1
- package/dist/query/rtk-query.browser.mjs +1 -1
- package/dist/query/rtk-query.browser.mjs.map +1 -1
- package/dist/query/rtk-query.legacy-esm.js +2175 -3014
- package/dist/query/rtk-query.legacy-esm.js.map +1 -1
- package/dist/query/rtk-query.modern.mjs +2052 -2949
- package/dist/query/rtk-query.modern.mjs.map +1 -1
- package/dist/react/cjs/redux-toolkit-react.development.cjs +30 -51
- package/dist/react/cjs/redux-toolkit-react.development.cjs.map +1 -1
- package/dist/react/cjs/redux-toolkit-react.production.min.cjs +1 -1
- package/dist/react/cjs/redux-toolkit-react.production.min.cjs.map +1 -1
- package/dist/react/index.d.mts +12 -15
- package/dist/react/index.d.ts +12 -15
- package/dist/react/redux-toolkit-react.browser.mjs +1 -1
- package/dist/react/redux-toolkit-react.browser.mjs.map +1 -1
- package/dist/react/redux-toolkit-react.legacy-esm.js +83 -44
- package/dist/react/redux-toolkit-react.legacy-esm.js.map +1 -1
- package/dist/react/redux-toolkit-react.modern.mjs +23 -25
- package/dist/react/redux-toolkit-react.modern.mjs.map +1 -1
- package/dist/redux-toolkit.browser.mjs +1 -2
- package/dist/redux-toolkit.browser.mjs.map +1 -1
- package/dist/redux-toolkit.legacy-esm.js +1748 -2201
- package/dist/redux-toolkit.legacy-esm.js.map +1 -1
- package/dist/redux-toolkit.modern.mjs +1670 -2171
- package/dist/redux-toolkit.modern.mjs.map +1 -1
- package/package.json +17 -33
- package/skills/build-modern-redux-apps/modern-redux/SKILL.md +23 -14
- package/skills/build-modern-redux-apps/modern-redux/references/store-lifetime.md +6 -6
- package/skills/build-modern-redux-apps/redux-dataflow/SKILL.md +13 -10
- package/skills/evolve-and-diagnose-redux-apps/debug-redux-toolkit-apps/SKILL.md +6 -6
- package/skills/evolve-and-diagnose-redux-apps/migrate-to-modern-redux/SKILL.md +7 -8
- package/skills/manage-server-data/adopt-rtk-query/SKILL.md +13 -11
- package/skills/model-redux-state/build-slices-and-selectors/SKILL.md +26 -14
- package/skills/model-redux-state/design-state-ownership/SKILL.md +11 -12
- package/skills/model-redux-state/design-state-ownership/references/state-ownership.md +6 -6
- package/skills/orchestrate-side-effects/handle-side-effects/SKILL.md +27 -23
- package/skills/orchestrate-side-effects/handle-side-effects/references/listener-workflows.md +4 -4
- package/src/autoBatchEnhancer.ts +5 -3
- package/src/combineSlices.ts +5 -5
- package/src/createAction.ts +11 -6
- package/src/createAsyncThunk.ts +14 -2
- package/src/createReducer.ts +1 -1
- package/src/createSlice.ts +9 -4
- package/src/dynamicMiddleware/index.ts +21 -5
- package/src/entities/models.ts +2 -1
- package/src/entities/sorted_state_adapter.ts +35 -24
- package/src/entities/unsorted_state_adapter.ts +1 -1
- package/src/immutableStateInvariantMiddleware.ts +34 -5
- package/src/listenerMiddleware/types.ts +10 -10
- package/src/mapBuilders.ts +2 -5
- package/src/matchers.ts +7 -1
- package/src/query/core/apiState.ts +1 -1
- package/src/query/core/buildInitiate.ts +19 -15
- package/src/query/core/buildMiddleware/cacheLifecycle.ts +3 -1
- package/src/query/core/buildMiddleware/index.ts +1 -1
- package/src/query/core/buildMiddleware/invalidationByTags.ts +4 -1
- package/src/query/core/buildMiddleware/polling.ts +12 -3
- package/src/query/core/buildMiddleware/types.ts +5 -2
- package/src/query/core/buildSlice.ts +2 -2
- package/src/query/core/buildThunks.ts +19 -0
- package/src/query/core/module.ts +9 -3
- package/src/query/createApi.ts +4 -1
- package/src/query/endpointDefinitions.ts +15 -9
- package/src/query/fetchBaseQuery.ts +1 -1
- package/src/query/react/buildHooks.ts +54 -51
- package/src/query/react/namedHooks.ts +28 -24
- package/src/query/utils/isAbsoluteUrl.ts +8 -3
- package/src/serializableStateInvariantMiddleware.ts +1 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@depup/reduxjs__toolkit",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.13.0-depup.0",
|
|
4
4
|
"description": "The official, opinionated, batteries-included toolset for efficient Redux development (with updated dependencies)",
|
|
5
5
|
"author": "Mark Erikson <mark@isquaredsoftware.com>",
|
|
6
6
|
"license": "MIT",
|
|
@@ -187,7 +187,7 @@
|
|
|
187
187
|
}
|
|
188
188
|
},
|
|
189
189
|
"devDependencies": {
|
|
190
|
-
"@arethetypeswrong/cli": "^0.18.
|
|
190
|
+
"@arethetypeswrong/cli": "^0.18.3",
|
|
191
191
|
"@babel/core": "^7.24.8",
|
|
192
192
|
"@babel/helper-module-imports": "^7.24.7",
|
|
193
193
|
"@microsoft/api-extractor": "^7.13.2",
|
|
@@ -205,48 +205,32 @@
|
|
|
205
205
|
"@types/query-string": "^6.3.0",
|
|
206
206
|
"@types/react": "^19.0.1",
|
|
207
207
|
"@types/react-dom": "^19.0.1",
|
|
208
|
-
"@typescript-eslint/eslint-plugin": "^6",
|
|
209
|
-
"@typescript-eslint/parser": "^6",
|
|
210
208
|
"axios": "^0.19.2",
|
|
211
|
-
"esbuild": "^0.25.1",
|
|
212
|
-
"esbuild-extra": "^0.4.0",
|
|
213
|
-
"eslint": "^7.25.0",
|
|
214
|
-
"eslint-config-prettier": "^9.1.0",
|
|
215
|
-
"eslint-config-react-app": "^7.0.1",
|
|
216
|
-
"eslint-plugin-flowtype": "^5.7.2",
|
|
217
|
-
"eslint-plugin-import": "^2.22.1",
|
|
218
|
-
"eslint-plugin-jsx-a11y": "^6.4.1",
|
|
219
|
-
"eslint-plugin-prettier": "^5.1.3",
|
|
220
|
-
"eslint-plugin-react": "^7.23.2",
|
|
221
|
-
"eslint-plugin-react-hooks": "^4.2.0",
|
|
222
209
|
"jsdom": "^25.0.1",
|
|
223
|
-
"msw": "
|
|
210
|
+
"msw": "2.1.5",
|
|
224
211
|
"node-fetch": "^3.3.2",
|
|
225
|
-
"prettier": "^3.2.5",
|
|
226
212
|
"query-string": "^7.0.1",
|
|
227
213
|
"react": "^19.0.0",
|
|
228
214
|
"react-dom": "^19.0.0",
|
|
229
215
|
"react-redux": "^9.2.0",
|
|
230
216
|
"rimraf": "^6.1.3",
|
|
231
217
|
"size-limit": "^11.0.1",
|
|
232
|
-
"
|
|
218
|
+
"tsdown": "^0.22.14",
|
|
233
219
|
"tsx": "^4.19.0",
|
|
234
|
-
"typescript": "^
|
|
220
|
+
"typescript": "^6.0.3",
|
|
221
|
+
"unrun": "^0.3.1",
|
|
235
222
|
"valibot": "^1.0.0",
|
|
236
223
|
"vite-tsconfig-paths": "^4.3.1",
|
|
237
|
-
"vitest": "^
|
|
224
|
+
"vitest": "^5"
|
|
238
225
|
},
|
|
239
226
|
"scripts": {
|
|
240
227
|
"clean": "rimraf dist",
|
|
241
|
-
"run-build": "
|
|
242
|
-
"build": "
|
|
243
|
-
"build-only": "
|
|
244
|
-
"format": "prettier --write \"(src|examples)/**/*.{ts,tsx}\" \"**/*.md\"",
|
|
245
|
-
"format:check": "prettier --list-different \"(src|examples)/**/*.{ts,tsx}\" \"docs/*/**.md\"",
|
|
246
|
-
"lint": "eslint src examples",
|
|
228
|
+
"run-build": "tsdown --config=./tsdown.config.mts --config-loader unrun",
|
|
229
|
+
"build": "pnpm clean && pnpm run-build && tsx scripts/fixUniqueSymbolExports.mts && tsx scripts/stripSourcemapFileField.mts",
|
|
230
|
+
"build-only": "pnpm clean && pnpm run-build",
|
|
247
231
|
"test": "vitest --typecheck --run ",
|
|
248
232
|
"test:watch": "vitest --watch",
|
|
249
|
-
"type-tests": "
|
|
233
|
+
"type-tests": "pnpm exec tsc -p tsconfig.test.json --noEmit",
|
|
250
234
|
"size": "size-limit"
|
|
251
235
|
},
|
|
252
236
|
"files": [
|
|
@@ -264,10 +248,10 @@
|
|
|
264
248
|
"dependencies": {
|
|
265
249
|
"@standard-schema/spec": "^1.1.0",
|
|
266
250
|
"@standard-schema/utils": "^0.3.0",
|
|
267
|
-
"immer": "^11.1.
|
|
251
|
+
"immer": "^11.1.18",
|
|
268
252
|
"redux": "^5.0.1",
|
|
269
253
|
"redux-thunk": "^3.1.0",
|
|
270
|
-
"reselect": "^5.
|
|
254
|
+
"reselect": "^5.3.0"
|
|
271
255
|
},
|
|
272
256
|
"peerDependencies": {
|
|
273
257
|
"react": "^16.9.0 || ^17.0.0 || ^18 || ^19",
|
|
@@ -294,17 +278,17 @@
|
|
|
294
278
|
},
|
|
295
279
|
"immer": {
|
|
296
280
|
"from": "^11.0.0",
|
|
297
|
-
"to": "^11.1.
|
|
281
|
+
"to": "^11.1.18"
|
|
298
282
|
},
|
|
299
283
|
"reselect": {
|
|
300
284
|
"from": "^5.1.0",
|
|
301
|
-
"to": "^5.
|
|
285
|
+
"to": "^5.3.0"
|
|
302
286
|
}
|
|
303
287
|
},
|
|
304
288
|
"depsUpdated": 3,
|
|
305
289
|
"originalPackage": "@reduxjs/toolkit",
|
|
306
|
-
"originalVersion": "2.
|
|
307
|
-
"processedAt": "2026-
|
|
290
|
+
"originalVersion": "2.13.0",
|
|
291
|
+
"processedAt": "2026-09-29T08:13:47.731Z",
|
|
308
292
|
"smokeTest": "passed"
|
|
309
293
|
}
|
|
310
294
|
}
|
|
@@ -6,16 +6,16 @@ description: >
|
|
|
6
6
|
hooks-first React-Redux usage, feature folders, and the correct store lifetime
|
|
7
7
|
for SPA and SSR-heavy React environments.
|
|
8
8
|
type: lifecycle
|
|
9
|
-
library:
|
|
10
|
-
library_version:
|
|
9
|
+
library: '@reduxjs/toolkit'
|
|
10
|
+
library_version: '2.11.2'
|
|
11
11
|
requires:
|
|
12
12
|
- build-modern-redux-apps/redux-dataflow
|
|
13
13
|
sources:
|
|
14
|
-
-
|
|
15
|
-
-
|
|
16
|
-
-
|
|
17
|
-
-
|
|
18
|
-
-
|
|
14
|
+
- 'reduxjs/redux-toolkit:docs/tutorials/quick-start.mdx'
|
|
15
|
+
- 'reduxjs/redux-toolkit:docs/tutorials/typescript.md'
|
|
16
|
+
- 'reduxjs/redux-toolkit:docs/usage/migrating-to-modern-redux.mdx'
|
|
17
|
+
- 'reduxjs/redux-toolkit:docs/usage/nextjs.mdx'
|
|
18
|
+
- 'reduxjs/redux:docs/style-guide/style-guide.md'
|
|
19
19
|
---
|
|
20
20
|
|
|
21
21
|
# Modern Redux
|
|
@@ -97,9 +97,7 @@ export function PostsList() {
|
|
|
97
97
|
|
|
98
98
|
return (
|
|
99
99
|
<button
|
|
100
|
-
onClick={() =>
|
|
101
|
-
dispatch(postAdded({ id: 'p2', title: 'Write docs' }))
|
|
102
|
-
}
|
|
100
|
+
onClick={() => dispatch(postAdded({ id: 'p2', title: 'Write docs' }))}
|
|
103
101
|
>
|
|
104
102
|
{posts.length}
|
|
105
103
|
</button>
|
|
@@ -128,7 +126,7 @@ export type RootState = ReturnType<AppStore['getState']>
|
|
|
128
126
|
export type AppDispatch = AppStore['dispatch']
|
|
129
127
|
|
|
130
128
|
// file: src/app/StoreProvider.tsx
|
|
131
|
-
'use client'
|
|
129
|
+
;('use client')
|
|
132
130
|
|
|
133
131
|
import { useState, type ReactNode } from 'react'
|
|
134
132
|
import { Provider } from 'react-redux'
|
|
@@ -204,7 +202,10 @@ const mapState = (state: { counter: { value: number } }) => ({
|
|
|
204
202
|
})
|
|
205
203
|
const mapDispatch = { increment }
|
|
206
204
|
|
|
207
|
-
function Counter({
|
|
205
|
+
function Counter({
|
|
206
|
+
value,
|
|
207
|
+
increment,
|
|
208
|
+
}: ReturnType<typeof mapState> & typeof mapDispatch) {
|
|
208
209
|
return <button onClick={() => increment()}>{value}</button>
|
|
209
210
|
}
|
|
210
211
|
|
|
@@ -239,7 +240,11 @@ Wrong:
|
|
|
239
240
|
import { Provider } from 'react-redux'
|
|
240
241
|
import { makeStore } from '../lib/store'
|
|
241
242
|
|
|
242
|
-
export function StoreProvider({
|
|
243
|
+
export function StoreProvider({
|
|
244
|
+
children,
|
|
245
|
+
}: {
|
|
246
|
+
children: import('react').ReactNode
|
|
247
|
+
}) {
|
|
243
248
|
const store = makeStore()
|
|
244
249
|
return <Provider store={store}>{children}</Provider>
|
|
245
250
|
}
|
|
@@ -254,7 +259,11 @@ import { useState } from 'react'
|
|
|
254
259
|
import { Provider } from 'react-redux'
|
|
255
260
|
import { makeStore } from '../lib/store'
|
|
256
261
|
|
|
257
|
-
export function StoreProvider({
|
|
262
|
+
export function StoreProvider({
|
|
263
|
+
children,
|
|
264
|
+
}: {
|
|
265
|
+
children: import('react').ReactNode
|
|
266
|
+
}) {
|
|
258
267
|
const [store] = useState(makeStore)
|
|
259
268
|
return <Provider store={store}>{children}</Provider>
|
|
260
269
|
}
|
|
@@ -2,11 +2,11 @@
|
|
|
2
2
|
|
|
3
3
|
## Decision table
|
|
4
4
|
|
|
5
|
-
| Environment
|
|
6
|
-
|
|
|
7
|
-
| Client-only SPA
|
|
8
|
-
| SSR-heavy React app
|
|
9
|
-
| Non-React integration code | Direct store access can be acceptable
|
|
5
|
+
| Environment | Default store shape | Why |
|
|
6
|
+
| -------------------------- | --------------------------------------- | ---------------------------------------------------------------------------------------------------- |
|
|
7
|
+
| Client-only SPA | One module-level singleton store | There is one browser session and no cross-request leakage risk. |
|
|
8
|
+
| SSR-heavy React app | `makeStore()` plus provider-local state | Each request needs its own store instance, but that instance must stay stable across client renders. |
|
|
9
|
+
| Non-React integration code | Direct store access can be acceptable | This is outside the React context boundary and should stay out of UI components. |
|
|
10
10
|
|
|
11
11
|
## SPA pattern
|
|
12
12
|
|
|
@@ -38,7 +38,7 @@ export const makeStore = () =>
|
|
|
38
38
|
})
|
|
39
39
|
|
|
40
40
|
// file: src/app/StoreProvider.tsx
|
|
41
|
-
'use client'
|
|
41
|
+
;('use client')
|
|
42
42
|
|
|
43
43
|
import { useState, type ReactNode } from 'react'
|
|
44
44
|
import { Provider } from 'react-redux'
|
|
@@ -5,12 +5,12 @@ description: >
|
|
|
5
5
|
event-style actions, reducer-owned state transitions, derived data, or a
|
|
6
6
|
debugging model for Redux Toolkit apps.
|
|
7
7
|
type: core
|
|
8
|
-
library:
|
|
9
|
-
library_version:
|
|
8
|
+
library: '@reduxjs/toolkit'
|
|
9
|
+
library_version: '2.11.2'
|
|
10
10
|
sources:
|
|
11
|
-
-
|
|
12
|
-
-
|
|
13
|
-
-
|
|
11
|
+
- 'reduxjs/redux:docs/tutorials/fundamentals/part-2-concepts-data-flow.md'
|
|
12
|
+
- 'reduxjs/redux:docs/tutorials/essentials/part-3-data-flow.md'
|
|
13
|
+
- 'reduxjs/redux:docs/style-guide/style-guide.md'
|
|
14
14
|
---
|
|
15
15
|
|
|
16
16
|
# Redux Dataflow
|
|
@@ -134,9 +134,7 @@ const selectFilter = (state: RootState) => state.posts.filter
|
|
|
134
134
|
export const selectVisiblePosts = createSelector(
|
|
135
135
|
[selectPosts, selectFilter],
|
|
136
136
|
(posts, filter) =>
|
|
137
|
-
filter === 'all'
|
|
138
|
-
? posts
|
|
139
|
-
: posts.filter((post) => post.published),
|
|
137
|
+
filter === 'all' ? posts : posts.filter((post) => post.published),
|
|
140
138
|
)
|
|
141
139
|
```
|
|
142
140
|
|
|
@@ -159,7 +157,9 @@ if (post) {
|
|
|
159
157
|
Correct:
|
|
160
158
|
|
|
161
159
|
```ts
|
|
162
|
-
store.dispatch(
|
|
160
|
+
store.dispatch(
|
|
161
|
+
postUpdated({ id: 'p1', changes: { title: 'Changed in place' } }),
|
|
162
|
+
)
|
|
163
163
|
```
|
|
164
164
|
|
|
165
165
|
Objects read from the store are still store state; mutating them outside reducers breaks immutability and stale-render assumptions.
|
|
@@ -171,7 +171,10 @@ Source: reduxjs/redux:docs/style-guide/style-guide.md
|
|
|
171
171
|
Wrong:
|
|
172
172
|
|
|
173
173
|
```ts
|
|
174
|
-
const nextPosts = [
|
|
174
|
+
const nextPosts = [
|
|
175
|
+
...selectPosts(store.getState()),
|
|
176
|
+
{ id: 'p2', title: 'Write docs' },
|
|
177
|
+
]
|
|
175
178
|
store.dispatch(setPosts(nextPosts))
|
|
176
179
|
```
|
|
177
180
|
|
|
@@ -6,15 +6,15 @@ description: >
|
|
|
6
6
|
Toolkit and RTK Query bugs. Covers a practical event -> reducer -> selector ->
|
|
7
7
|
render debugging loop plus RTK Query cache interpretation.
|
|
8
8
|
type: lifecycle
|
|
9
|
-
library:
|
|
10
|
-
library_version:
|
|
9
|
+
library: '@reduxjs/toolkit'
|
|
10
|
+
library_version: '2.11.2'
|
|
11
11
|
requires:
|
|
12
12
|
- build-modern-redux-apps/redux-dataflow
|
|
13
13
|
sources:
|
|
14
|
-
-
|
|
15
|
-
-
|
|
16
|
-
-
|
|
17
|
-
-
|
|
14
|
+
- 'reduxjs/redux:docs/style-guide/style-guide.md'
|
|
15
|
+
- 'reduxjs/redux:docs/tutorials/essentials/part-5-async-logic.md'
|
|
16
|
+
- 'reduxjs/redux:docs/tutorials/essentials/part-8-rtk-query-advanced.md'
|
|
17
|
+
- 'reduxjs/redux-toolkit:docs/usage/usage-guide.md'
|
|
18
18
|
---
|
|
19
19
|
|
|
20
20
|
# Debug Redux Toolkit Apps
|
|
@@ -6,15 +6,15 @@ description: >
|
|
|
6
6
|
createSlice, codemod-assisted RTK 2 updates, and replacing server-data stacks
|
|
7
7
|
with RTK Query instead of writing new legacy Redux code.
|
|
8
8
|
type: lifecycle
|
|
9
|
-
library:
|
|
10
|
-
library_version:
|
|
9
|
+
library: '@reduxjs/toolkit'
|
|
10
|
+
library_version: '2.11.2'
|
|
11
11
|
requires:
|
|
12
12
|
- build-modern-redux-apps/modern-redux
|
|
13
13
|
sources:
|
|
14
|
-
-
|
|
15
|
-
-
|
|
16
|
-
-
|
|
17
|
-
-
|
|
14
|
+
- 'reduxjs/redux-toolkit:docs/usage/migrating-to-modern-redux.mdx'
|
|
15
|
+
- 'reduxjs/redux-toolkit:docs/usage/migrating-rtk-2.md'
|
|
16
|
+
- 'reduxjs/redux-toolkit:packages/rtk-codemods/README.md'
|
|
17
|
+
- 'reduxjs/redux:docs/style-guide/style-guide.md'
|
|
18
18
|
---
|
|
19
19
|
|
|
20
20
|
# Migrate To Modern Redux
|
|
@@ -174,8 +174,7 @@ Correct:
|
|
|
174
174
|
```ts
|
|
175
175
|
configureStore({
|
|
176
176
|
reducer,
|
|
177
|
-
middleware: (getDefaultMiddleware) =>
|
|
178
|
-
getDefaultMiddleware().concat(logger),
|
|
177
|
+
middleware: (getDefaultMiddleware) => getDefaultMiddleware().concat(logger),
|
|
179
178
|
})
|
|
180
179
|
```
|
|
181
180
|
|
|
@@ -5,17 +5,17 @@ description: >
|
|
|
5
5
|
layer. Covers createApi, store integration, hooks, invalidation behavior,
|
|
6
6
|
optimistic updates, and deciding when RTK Query is the right cache model.
|
|
7
7
|
type: lifecycle
|
|
8
|
-
library:
|
|
9
|
-
library_version:
|
|
8
|
+
library: '@reduxjs/toolkit'
|
|
9
|
+
library_version: '2.11.2'
|
|
10
10
|
requires:
|
|
11
11
|
- build-modern-redux-apps/modern-redux
|
|
12
12
|
sources:
|
|
13
|
-
-
|
|
14
|
-
-
|
|
15
|
-
-
|
|
16
|
-
-
|
|
17
|
-
-
|
|
18
|
-
-
|
|
13
|
+
- 'reduxjs/redux-toolkit:docs/rtk-query/api/createApi.mdx'
|
|
14
|
+
- 'reduxjs/redux-toolkit:docs/rtk-query/usage/automated-refetching.mdx'
|
|
15
|
+
- 'reduxjs/redux-toolkit:docs/rtk-query/usage/manual-cache-updates.mdx'
|
|
16
|
+
- 'reduxjs/redux-toolkit:docs/rtk-query/usage/persistence-and-rehydration.mdx'
|
|
17
|
+
- 'reduxjs/redux-toolkit:docs/tutorials/rtk-query.mdx'
|
|
18
|
+
- 'reduxjs/redux:docs/style-guide/style-guide.md'
|
|
19
19
|
---
|
|
20
20
|
|
|
21
21
|
# Adopt RTK Query
|
|
@@ -315,9 +315,11 @@ import { useAppDispatch } from '../../app/hooks'
|
|
|
315
315
|
const dispatch = useAppDispatch()
|
|
316
316
|
|
|
317
317
|
useEffect(() => {
|
|
318
|
-
dispatch(
|
|
319
|
-
|
|
320
|
-
|
|
318
|
+
dispatch(
|
|
319
|
+
api.util.updateQueryData('getPosts', undefined, (draft) => {
|
|
320
|
+
draft.push({ id: 'p3', title: 'Patched from component' })
|
|
321
|
+
}),
|
|
322
|
+
)
|
|
321
323
|
}, [dispatch])
|
|
322
324
|
```
|
|
323
325
|
|
|
@@ -6,17 +6,17 @@ description: >
|
|
|
6
6
|
Immer-backed mutation syntax, slice selectors, getSelectors, injectInto,
|
|
7
7
|
withLazyLoadedSlices, and current RTK 2 slice patterns.
|
|
8
8
|
type: core
|
|
9
|
-
library:
|
|
10
|
-
library_version:
|
|
9
|
+
library: '@reduxjs/toolkit'
|
|
10
|
+
library_version: '2.11.2'
|
|
11
11
|
requires:
|
|
12
12
|
- model-redux-state/design-state-ownership
|
|
13
13
|
sources:
|
|
14
|
-
-
|
|
15
|
-
-
|
|
16
|
-
-
|
|
17
|
-
-
|
|
18
|
-
-
|
|
19
|
-
-
|
|
14
|
+
- 'reduxjs/redux-toolkit:docs/api/createSlice.mdx'
|
|
15
|
+
- 'reduxjs/redux-toolkit:docs/api/combineSlices.mdx'
|
|
16
|
+
- 'reduxjs/redux-toolkit:docs/api/createEntityAdapter.mdx'
|
|
17
|
+
- 'reduxjs/redux-toolkit:docs/usage/immer-reducers.md'
|
|
18
|
+
- 'reduxjs/redux-toolkit:docs/usage/migrating-rtk-2.md'
|
|
19
|
+
- 'reduxjs/redux:docs/style-guide/style-guide.md'
|
|
20
20
|
---
|
|
21
21
|
|
|
22
22
|
# Build Slices And Selectors
|
|
@@ -49,13 +49,19 @@ export const postsSlice = createAppSlice({
|
|
|
49
49
|
name: 'posts',
|
|
50
50
|
initialState,
|
|
51
51
|
reducers: (create) => ({
|
|
52
|
-
postAdded: create.reducer<{ id: string; title: string }>(
|
|
53
|
-
state
|
|
54
|
-
|
|
52
|
+
postAdded: create.reducer<{ id: string; title: string }>(
|
|
53
|
+
(state, action) => {
|
|
54
|
+
state.items.push({ ...action.payload, published: false })
|
|
55
|
+
},
|
|
56
|
+
),
|
|
55
57
|
fetchPosts: create.asyncThunk(
|
|
56
58
|
async () => {
|
|
57
59
|
const response = await fetch('/api/posts')
|
|
58
|
-
return (await response.json()) as {
|
|
60
|
+
return (await response.json()) as {
|
|
61
|
+
id: string
|
|
62
|
+
title: string
|
|
63
|
+
published: boolean
|
|
64
|
+
}[]
|
|
59
65
|
},
|
|
60
66
|
{
|
|
61
67
|
pending: (state) => {
|
|
@@ -145,7 +151,10 @@ const createAppSlice = buildCreateSlice({
|
|
|
145
151
|
|
|
146
152
|
const usersSlice = createAppSlice({
|
|
147
153
|
name: 'users',
|
|
148
|
-
initialState: {
|
|
154
|
+
initialState: {
|
|
155
|
+
items: [] as { id: string; name: string }[],
|
|
156
|
+
status: 'idle' as 'idle' | 'pending' | 'failed',
|
|
157
|
+
},
|
|
149
158
|
reducers: (create) => ({
|
|
150
159
|
fetchUsers: create.asyncThunk(
|
|
151
160
|
async () => {
|
|
@@ -253,7 +262,10 @@ Source: reduxjs/redux-toolkit:docs/usage/immer-reducers.md
|
|
|
253
262
|
Wrong:
|
|
254
263
|
|
|
255
264
|
```ts
|
|
256
|
-
export default function todosReducer(
|
|
265
|
+
export default function todosReducer(
|
|
266
|
+
state = initialState,
|
|
267
|
+
action: { type: string; payload?: Todo },
|
|
268
|
+
) {
|
|
257
269
|
switch (action.type) {
|
|
258
270
|
case 'todos/todoAdded':
|
|
259
271
|
return state.concat(action.payload as Todo)
|
|
@@ -5,12 +5,12 @@ description: >
|
|
|
5
5
|
state, or another external source. Covers state ownership, authority
|
|
6
6
|
boundaries, slice sizing, and when to move or split data as the app evolves.
|
|
7
7
|
type: core
|
|
8
|
-
library:
|
|
9
|
-
library_version:
|
|
8
|
+
library: '@reduxjs/toolkit'
|
|
9
|
+
library_version: '2.11.2'
|
|
10
10
|
sources:
|
|
11
|
-
-
|
|
12
|
-
-
|
|
13
|
-
-
|
|
11
|
+
- 'reduxjs/redux:docs/style-guide/style-guide.md'
|
|
12
|
+
- 'reduxjs/redux:docs/tutorials/essentials/part-2-app-structure.md'
|
|
13
|
+
- 'reduxjs/redux:docs/tutorials/essentials/part-4-using-data.md'
|
|
14
14
|
---
|
|
15
15
|
|
|
16
16
|
# Design State Ownership
|
|
@@ -110,9 +110,7 @@ const selectPosts = (state: RootState) => state.posts.items
|
|
|
110
110
|
const selectVisiblePosts = createSelector(
|
|
111
111
|
[selectPosts, (_state: RootState, filter: string) => filter],
|
|
112
112
|
(posts, filter) =>
|
|
113
|
-
filter === 'published'
|
|
114
|
-
? posts.filter((post) => post.published)
|
|
115
|
-
: posts,
|
|
113
|
+
filter === 'published' ? posts.filter((post) => post.published) : posts,
|
|
116
114
|
)
|
|
117
115
|
|
|
118
116
|
export function PostsList() {
|
|
@@ -160,7 +158,8 @@ Wrong:
|
|
|
160
158
|
```tsx
|
|
161
159
|
import { useAppSelector } from '../../app/hooks'
|
|
162
160
|
|
|
163
|
-
const selectDraftTitle = (state: { draft: { title: string } }) =>
|
|
161
|
+
const selectDraftTitle = (state: { draft: { title: string } }) =>
|
|
162
|
+
state.draft.title
|
|
164
163
|
|
|
165
164
|
const title = useAppSelector(selectDraftTitle)
|
|
166
165
|
```
|
|
@@ -190,9 +189,9 @@ function PostsPage() {
|
|
|
190
189
|
const [searchParams] = useSearchParams()
|
|
191
190
|
const dispatch = useAppDispatch()
|
|
192
191
|
|
|
193
|
-
useEffect(() => {
|
|
194
|
-
|
|
195
|
-
}, [dispatch, searchParams])
|
|
192
|
+
useEffect(() => {
|
|
193
|
+
dispatch(filterChanged(searchParams.get('filter') ?? 'all'))
|
|
194
|
+
}, [dispatch, searchParams])
|
|
196
195
|
|
|
197
196
|
return null
|
|
198
197
|
}
|
|
@@ -2,12 +2,12 @@
|
|
|
2
2
|
|
|
3
3
|
## Choose the owner, then the tool
|
|
4
4
|
|
|
5
|
-
| Kind of state
|
|
6
|
-
|
|
|
7
|
-
| Editable form fields
|
|
8
|
-
| Shared mutable app data
|
|
9
|
-
| Server cache
|
|
10
|
-
| URL, pathname, search params
|
|
5
|
+
| Kind of state | Default owner | Typical tool |
|
|
6
|
+
| ------------------------------------------ | --------------- | ---------------------------------------- |
|
|
7
|
+
| Editable form fields | Component | `useState` |
|
|
8
|
+
| Shared mutable app data | Redux | Slice state |
|
|
9
|
+
| Server cache | RTK Query | `createApi` |
|
|
10
|
+
| URL, pathname, search params | Router | Router APIs plus selector inputs |
|
|
11
11
|
| Browser-only authority like `localStorage` | External source | Read at boundaries, then dispatch events |
|
|
12
12
|
|
|
13
13
|
## Good reasons to move data into Redux
|
|
@@ -6,15 +6,15 @@ description: >
|
|
|
6
6
|
workflows, listener middleware setup, and keeping side effects out of
|
|
7
7
|
reducers and UI components.
|
|
8
8
|
type: core
|
|
9
|
-
library:
|
|
10
|
-
library_version:
|
|
9
|
+
library: '@reduxjs/toolkit'
|
|
10
|
+
library_version: '2.11.2'
|
|
11
11
|
requires:
|
|
12
12
|
- build-modern-redux-apps/redux-dataflow
|
|
13
13
|
sources:
|
|
14
|
-
-
|
|
15
|
-
-
|
|
16
|
-
-
|
|
17
|
-
-
|
|
14
|
+
- 'reduxjs/redux-toolkit:docs/api/createAsyncThunk.mdx'
|
|
15
|
+
- 'reduxjs/redux-toolkit:docs/api/createListenerMiddleware.mdx'
|
|
16
|
+
- 'reduxjs/redux:docs/style-guide/style-guide.md'
|
|
17
|
+
- 'reduxjs/redux:docs/tutorials/essentials/part-5-async-logic.md'
|
|
18
18
|
---
|
|
19
19
|
|
|
20
20
|
# Handle Side Effects
|
|
@@ -54,8 +54,10 @@ export const store = configureStore({
|
|
|
54
54
|
export type RootState = ReturnType<typeof store.getState>
|
|
55
55
|
export type AppDispatch = typeof store.dispatch
|
|
56
56
|
|
|
57
|
-
export const startAppListening =
|
|
58
|
-
|
|
57
|
+
export const startAppListening = listenerMiddleware.startListening.withTypes<
|
|
58
|
+
RootState,
|
|
59
|
+
AppDispatch
|
|
60
|
+
>()
|
|
59
61
|
```
|
|
60
62
|
|
|
61
63
|
## Core Patterns
|
|
@@ -186,14 +188,17 @@ Correct:
|
|
|
186
188
|
```ts
|
|
187
189
|
import { createAsyncThunk } from '@reduxjs/toolkit'
|
|
188
190
|
|
|
189
|
-
const todoSaved = createAsyncThunk(
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
})
|
|
191
|
+
const todoSaved = createAsyncThunk(
|
|
192
|
+
'todos/save',
|
|
193
|
+
async (todo: { id: string }) => {
|
|
194
|
+
await fetch('/api/todos', {
|
|
195
|
+
method: 'POST',
|
|
196
|
+
headers: { 'Content-Type': 'application/json' },
|
|
197
|
+
body: JSON.stringify(todo),
|
|
198
|
+
})
|
|
199
|
+
return todo
|
|
200
|
+
},
|
|
201
|
+
)
|
|
197
202
|
```
|
|
198
203
|
|
|
199
204
|
Reducers must stay pure even when Immer is available.
|
|
@@ -205,14 +210,13 @@ Source: reduxjs/redux:docs/style-guide/style-guide.md
|
|
|
205
210
|
Wrong:
|
|
206
211
|
|
|
207
212
|
```ts
|
|
208
|
-
export const waitForSave =
|
|
209
|
-
|
|
210
|
-
getState: () => { docs: { status: string } }
|
|
211
|
-
)
|
|
212
|
-
|
|
213
|
-
|
|
213
|
+
export const waitForSave =
|
|
214
|
+
() =>
|
|
215
|
+
async (_dispatch: unknown, getState: () => { docs: { status: string } }) => {
|
|
216
|
+
while (getState().docs.status !== 'saved') {
|
|
217
|
+
await new Promise((resolve) => setTimeout(resolve, 100))
|
|
218
|
+
}
|
|
214
219
|
}
|
|
215
|
-
}
|
|
216
220
|
```
|
|
217
221
|
|
|
218
222
|
Correct:
|
package/skills/orchestrate-side-effects/handle-side-effects/references/listener-workflows.md
CHANGED
|
@@ -2,11 +2,11 @@
|
|
|
2
2
|
|
|
3
3
|
## Decision table
|
|
4
4
|
|
|
5
|
-
| Need
|
|
6
|
-
|
|
|
7
|
-
| Cached server data
|
|
5
|
+
| Need | Reach for |
|
|
6
|
+
| ---------------------------------------------------------- | ----------------------------- |
|
|
7
|
+
| Cached server data | RTK Query |
|
|
8
8
|
| One imperative async workflow with `dispatch` / `getState` | `createAsyncThunk` or a thunk |
|
|
9
|
-
| React to later actions or state transitions
|
|
9
|
+
| React to later actions or state transitions | `createListenerMiddleware` |
|
|
10
10
|
|
|
11
11
|
A good app often mixes imperative and reactive workflows. The split is by job, not by ideology.
|
|
12
12
|
|
package/src/autoBatchEnhancer.ts
CHANGED
|
@@ -21,6 +21,8 @@ const createRafWithFallbackTimer = (
|
|
|
21
21
|
) => {
|
|
22
22
|
return (notify: () => void) => {
|
|
23
23
|
let called = false
|
|
24
|
+
let rafId: number
|
|
25
|
+
let timerId: ReturnType<typeof setTimeout>
|
|
24
26
|
const callback = () => {
|
|
25
27
|
if (called) return
|
|
26
28
|
called = true
|
|
@@ -28,8 +30,8 @@ const createRafWithFallbackTimer = (
|
|
|
28
30
|
clearTimeout(timerId)
|
|
29
31
|
notify()
|
|
30
32
|
}
|
|
31
|
-
|
|
32
|
-
|
|
33
|
+
rafId = raf(callback)
|
|
34
|
+
timerId = setTimeout(callback, timeout)
|
|
33
35
|
}
|
|
34
36
|
}
|
|
35
37
|
|
|
@@ -52,7 +54,7 @@ export type AutoBatchOptions =
|
|
|
52
54
|
* This can be added to `action.meta` manually, or by using the
|
|
53
55
|
* `prepareAutoBatched` helper.
|
|
54
56
|
*
|
|
55
|
-
* By default, it will queue a notification
|
|
57
|
+
* By default, it will queue a notification using `requestAnimationFrame`.
|
|
56
58
|
* However, you can pass several other options to configure the behavior:
|
|
57
59
|
* - `{type: 'tick'}`: queues using `queueMicrotask`
|
|
58
60
|
* - `{type: 'timer', timeout: number}`: queues using `setTimeout`
|