@depup/reduxjs__toolkit 2.12.0-depup.28

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.
Files changed (173) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +33 -0
  3. package/changes.json +18 -0
  4. package/dist/cjs/index.js +6 -0
  5. package/dist/cjs/redux-toolkit.development.cjs +2406 -0
  6. package/dist/cjs/redux-toolkit.development.cjs.map +1 -0
  7. package/dist/cjs/redux-toolkit.production.min.cjs +3 -0
  8. package/dist/cjs/redux-toolkit.production.min.cjs.map +1 -0
  9. package/dist/index.d.mts +2665 -0
  10. package/dist/index.d.ts +2665 -0
  11. package/dist/query/cjs/index.js +6 -0
  12. package/dist/query/cjs/rtk-query.development.cjs +3092 -0
  13. package/dist/query/cjs/rtk-query.development.cjs.map +1 -0
  14. package/dist/query/cjs/rtk-query.production.min.cjs +2 -0
  15. package/dist/query/cjs/rtk-query.production.min.cjs.map +1 -0
  16. package/dist/query/index.d.mts +3066 -0
  17. package/dist/query/index.d.ts +3066 -0
  18. package/dist/query/react/cjs/index.js +6 -0
  19. package/dist/query/react/cjs/rtk-query-react.development.cjs +748 -0
  20. package/dist/query/react/cjs/rtk-query-react.development.cjs.map +1 -0
  21. package/dist/query/react/cjs/rtk-query-react.production.min.cjs +2 -0
  22. package/dist/query/react/cjs/rtk-query-react.production.min.cjs.map +1 -0
  23. package/dist/query/react/index.d.mts +1009 -0
  24. package/dist/query/react/index.d.ts +1009 -0
  25. package/dist/query/react/rtk-query-react.browser.mjs +2 -0
  26. package/dist/query/react/rtk-query-react.browser.mjs.map +1 -0
  27. package/dist/query/react/rtk-query-react.legacy-esm.js +740 -0
  28. package/dist/query/react/rtk-query-react.legacy-esm.js.map +1 -0
  29. package/dist/query/react/rtk-query-react.modern.mjs +705 -0
  30. package/dist/query/react/rtk-query-react.modern.mjs.map +1 -0
  31. package/dist/query/rtk-query.browser.mjs +2 -0
  32. package/dist/query/rtk-query.browser.mjs.map +1 -0
  33. package/dist/query/rtk-query.legacy-esm.js +3117 -0
  34. package/dist/query/rtk-query.legacy-esm.js.map +1 -0
  35. package/dist/query/rtk-query.modern.mjs +3052 -0
  36. package/dist/query/rtk-query.modern.mjs.map +1 -0
  37. package/dist/react/cjs/index.js +6 -0
  38. package/dist/react/cjs/redux-toolkit-react.development.cjs +55 -0
  39. package/dist/react/cjs/redux-toolkit-react.development.cjs.map +1 -0
  40. package/dist/react/cjs/redux-toolkit-react.production.min.cjs +2 -0
  41. package/dist/react/cjs/redux-toolkit-react.production.min.cjs.map +1 -0
  42. package/dist/react/index.d.mts +22 -0
  43. package/dist/react/index.d.ts +22 -0
  44. package/dist/react/redux-toolkit-react.browser.mjs +2 -0
  45. package/dist/react/redux-toolkit-react.browser.mjs.map +1 -0
  46. package/dist/react/redux-toolkit-react.legacy-esm.js +47 -0
  47. package/dist/react/redux-toolkit-react.legacy-esm.js.map +1 -0
  48. package/dist/react/redux-toolkit-react.modern.mjs +28 -0
  49. package/dist/react/redux-toolkit-react.modern.mjs.map +1 -0
  50. package/dist/redux-toolkit.browser.mjs +3 -0
  51. package/dist/redux-toolkit.browser.mjs.map +1 -0
  52. package/dist/redux-toolkit.legacy-esm.js +2351 -0
  53. package/dist/redux-toolkit.legacy-esm.js.map +1 -0
  54. package/dist/redux-toolkit.modern.mjs +2330 -0
  55. package/dist/redux-toolkit.modern.mjs.map +1 -0
  56. package/dist/uncheckedindexed.ts +16 -0
  57. package/package.json +310 -0
  58. package/query/package.json +13 -0
  59. package/query/react/package.json +13 -0
  60. package/react/package.json +13 -0
  61. package/skills/build-modern-redux-apps/modern-redux/SKILL.md +304 -0
  62. package/skills/build-modern-redux-apps/modern-redux/references/store-lifetime.md +53 -0
  63. package/skills/build-modern-redux-apps/redux-dataflow/SKILL.md +264 -0
  64. package/skills/evolve-and-diagnose-redux-apps/debug-redux-toolkit-apps/SKILL.md +269 -0
  65. package/skills/evolve-and-diagnose-redux-apps/migrate-to-modern-redux/SKILL.md +226 -0
  66. package/skills/manage-server-data/adopt-rtk-query/SKILL.md +385 -0
  67. package/skills/manage-server-data/adopt-rtk-query/references/endpoint-lifecycle.md +36 -0
  68. package/skills/model-redux-state/build-slices-and-selectors/SKILL.md +364 -0
  69. package/skills/model-redux-state/build-slices-and-selectors/references/slice-patterns.md +59 -0
  70. package/skills/model-redux-state/design-state-ownership/SKILL.md +322 -0
  71. package/skills/model-redux-state/design-state-ownership/references/state-ownership.md +28 -0
  72. package/skills/orchestrate-side-effects/handle-side-effects/SKILL.md +271 -0
  73. package/skills/orchestrate-side-effects/handle-side-effects/references/listener-workflows.md +34 -0
  74. package/src/actionCreatorInvariantMiddleware.ts +34 -0
  75. package/src/autoBatchEnhancer.ts +146 -0
  76. package/src/combineSlices.ts +487 -0
  77. package/src/configureStore.ts +248 -0
  78. package/src/createAction.ts +324 -0
  79. package/src/createAsyncThunk.ts +791 -0
  80. package/src/createDraftSafeSelector.ts +30 -0
  81. package/src/createReducer.ts +217 -0
  82. package/src/createSlice.ts +1079 -0
  83. package/src/devtoolsExtension.ts +241 -0
  84. package/src/dynamicMiddleware/index.ts +93 -0
  85. package/src/dynamicMiddleware/react/index.ts +101 -0
  86. package/src/dynamicMiddleware/types.ts +82 -0
  87. package/src/entities/create_adapter.ts +47 -0
  88. package/src/entities/entity_state.ts +38 -0
  89. package/src/entities/index.ts +8 -0
  90. package/src/entities/models.ts +198 -0
  91. package/src/entities/sorted_state_adapter.ts +266 -0
  92. package/src/entities/state_adapter.ts +58 -0
  93. package/src/entities/state_selectors.ts +73 -0
  94. package/src/entities/unsorted_state_adapter.ts +204 -0
  95. package/src/entities/utils.ts +68 -0
  96. package/src/formatProdErrorMessage.ts +13 -0
  97. package/src/getDefaultEnhancers.ts +31 -0
  98. package/src/getDefaultMiddleware.ts +113 -0
  99. package/src/immerImports.ts +7 -0
  100. package/src/immutableStateInvariantMiddleware.ts +274 -0
  101. package/src/index.ts +213 -0
  102. package/src/listenerMiddleware/exceptions.ts +20 -0
  103. package/src/listenerMiddleware/index.ts +562 -0
  104. package/src/listenerMiddleware/task.ts +100 -0
  105. package/src/listenerMiddleware/types.ts +886 -0
  106. package/src/listenerMiddleware/utils.ts +30 -0
  107. package/src/mapBuilders.ts +298 -0
  108. package/src/matchers.ts +365 -0
  109. package/src/nanoid.ts +20 -0
  110. package/src/query/HandledError.ts +6 -0
  111. package/src/query/apiTypes.ts +120 -0
  112. package/src/query/baseQueryTypes.ts +101 -0
  113. package/src/query/core/apiState.ts +377 -0
  114. package/src/query/core/buildInitiate.ts +596 -0
  115. package/src/query/core/buildMiddleware/batchActions.ts +207 -0
  116. package/src/query/core/buildMiddleware/cacheCollection.ts +204 -0
  117. package/src/query/core/buildMiddleware/cacheLifecycle.ts +379 -0
  118. package/src/query/core/buildMiddleware/devMiddleware.ts +34 -0
  119. package/src/query/core/buildMiddleware/index.ts +162 -0
  120. package/src/query/core/buildMiddleware/invalidationByTags.ts +139 -0
  121. package/src/query/core/buildMiddleware/polling.ts +187 -0
  122. package/src/query/core/buildMiddleware/queryLifecycle.ts +508 -0
  123. package/src/query/core/buildMiddleware/types.ts +173 -0
  124. package/src/query/core/buildMiddleware/windowEventHandling.ts +64 -0
  125. package/src/query/core/buildSelectors.ts +413 -0
  126. package/src/query/core/buildSlice.ts +735 -0
  127. package/src/query/core/buildThunks.ts +1124 -0
  128. package/src/query/core/index.ts +58 -0
  129. package/src/query/core/module.ts +727 -0
  130. package/src/query/core/rtkImports.ts +24 -0
  131. package/src/query/core/setupListeners.ts +118 -0
  132. package/src/query/createApi.ts +506 -0
  133. package/src/query/defaultSerializeQueryArgs.ts +52 -0
  134. package/src/query/endpointDefinitions.ts +1717 -0
  135. package/src/query/fakeBaseQuery.ts +21 -0
  136. package/src/query/fetchBaseQuery.ts +385 -0
  137. package/src/query/index.ts +105 -0
  138. package/src/query/react/ApiProvider.tsx +69 -0
  139. package/src/query/react/buildHooks.ts +2297 -0
  140. package/src/query/react/constants.ts +2 -0
  141. package/src/query/react/index.ts +45 -0
  142. package/src/query/react/module.ts +275 -0
  143. package/src/query/react/namedHooks.ts +59 -0
  144. package/src/query/react/reactImports.ts +10 -0
  145. package/src/query/react/reactReduxImports.ts +1 -0
  146. package/src/query/react/rtkqImports.ts +8 -0
  147. package/src/query/react/useSerializedStableValue.ts +17 -0
  148. package/src/query/react/useShallowStableValue.ts +13 -0
  149. package/src/query/retry.ts +233 -0
  150. package/src/query/standardSchema.ts +35 -0
  151. package/src/query/tsHelpers.ts +48 -0
  152. package/src/query/utils/capitalize.ts +3 -0
  153. package/src/query/utils/copyWithStructuralSharing.ts +27 -0
  154. package/src/query/utils/countObjectKeys.ts +14 -0
  155. package/src/query/utils/filterMap.ts +27 -0
  156. package/src/query/utils/getCurrent.ts +6 -0
  157. package/src/query/utils/getOrInsert.ts +41 -0
  158. package/src/query/utils/immerImports.ts +9 -0
  159. package/src/query/utils/index.ts +11 -0
  160. package/src/query/utils/isAbsoluteUrl.ts +9 -0
  161. package/src/query/utils/isDocumentVisible.ts +12 -0
  162. package/src/query/utils/isNotNullish.ts +7 -0
  163. package/src/query/utils/isOnline.ts +12 -0
  164. package/src/query/utils/isValidUrl.ts +9 -0
  165. package/src/query/utils/joinUrls.ts +26 -0
  166. package/src/query/utils/signals.ts +33 -0
  167. package/src/react/index.ts +7 -0
  168. package/src/reduxImports.ts +8 -0
  169. package/src/reselectImports.ts +1 -0
  170. package/src/serializableStateInvariantMiddleware.ts +285 -0
  171. package/src/tsHelpers.ts +214 -0
  172. package/src/uncheckedindexed.ts +16 -0
  173. package/src/utils.ts +125 -0
@@ -0,0 +1,226 @@
1
+ ---
2
+ name: evolve-and-diagnose-redux-apps/migrate-to-modern-redux
3
+ description: >
4
+ Use this when moving a legacy Redux codebase to current RTK patterns. Covers
5
+ replacing createStore with configureStore, migrating touched reducers to
6
+ createSlice, codemod-assisted RTK 2 updates, and replacing server-data stacks
7
+ with RTK Query instead of writing new legacy Redux code.
8
+ type: lifecycle
9
+ library: "@reduxjs/toolkit"
10
+ library_version: "2.11.2"
11
+ requires:
12
+ - build-modern-redux-apps/modern-redux
13
+ sources:
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
+ ---
19
+
20
+ # Migrate To Modern Redux
21
+
22
+ ## Setup
23
+
24
+ ```ts
25
+ // before
26
+ import { applyMiddleware, combineReducers, createStore } from 'redux'
27
+ import thunk from 'redux-thunk'
28
+
29
+ const postsReducer = (state = [] as { id: string; title: string }[]) => state
30
+ const usersReducer = (state = [] as { id: string; name: string }[]) => state
31
+
32
+ const rootReducer = combineReducers({
33
+ posts: postsReducer,
34
+ users: usersReducer,
35
+ })
36
+
37
+ export const legacyStore = createStore(rootReducer, applyMiddleware(thunk))
38
+
39
+ // after
40
+ import { configureStore } from '@reduxjs/toolkit'
41
+
42
+ const postsReducer = (state = [] as { id: string; title: string }[]) => state
43
+ const usersReducer = (state = [] as { id: string; name: string }[]) => state
44
+
45
+ export const store = configureStore({
46
+ reducer: {
47
+ posts: postsReducer,
48
+ users: usersReducer,
49
+ },
50
+ })
51
+ ```
52
+
53
+ ## Core Patterns
54
+
55
+ ### Replace the store setup first
56
+
57
+ ```ts
58
+ import { configureStore } from '@reduxjs/toolkit'
59
+
60
+ const postsReducer = (state = [] as { id: string; title: string }[]) => state
61
+ const usersReducer = (state = [] as { id: string; name: string }[]) => state
62
+
63
+ export const store = configureStore({
64
+ reducer: {
65
+ posts: postsReducer,
66
+ users: usersReducer,
67
+ },
68
+ })
69
+
70
+ export type RootState = ReturnType<typeof store.getState>
71
+ export type AppDispatch = typeof store.dispatch
72
+ ```
73
+
74
+ This is the one migration step that can happen immediately while old reducers continue to work.
75
+
76
+ ### Migrate reducers as you touch them
77
+
78
+ ```ts
79
+ import { createSlice } from '@reduxjs/toolkit'
80
+
81
+ type TodosState = {
82
+ items: { id: string; text: string; completed: boolean }[]
83
+ }
84
+
85
+ const initialState: TodosState = {
86
+ items: [],
87
+ }
88
+
89
+ export const todosSlice = createSlice({
90
+ name: 'todos',
91
+ initialState,
92
+ reducers: {
93
+ todoAdded(state, action: { payload: { id: string; text: string } }) {
94
+ state.items.push({ ...action.payload, completed: false })
95
+ },
96
+ todoToggled(state, action: { payload: { id: string } }) {
97
+ const todo = state.items.find((item) => item.id === action.payload.id)
98
+ if (todo) {
99
+ todo.completed = !todo.completed
100
+ }
101
+ },
102
+ },
103
+ })
104
+ ```
105
+
106
+ Once a reducer needs editing, migrate that reducer instead of adding more legacy code to it.
107
+
108
+ ### Use codemods for mechanical updates
109
+
110
+ ```bash
111
+ npx @reduxjs/rtk-codemods createSliceBuilder src/features/posts/postsSlice.ts
112
+ npx @reduxjs/rtk-codemods createReducerBuilder src/features/posts/postsReducer.ts
113
+ ```
114
+
115
+ Use codemods for repetitive RTK API migrations, then review the result and finish the semantic cleanup by hand.
116
+
117
+ ### Replace legacy server-data stacks with RTK Query
118
+
119
+ ```ts
120
+ import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react'
121
+
122
+ type Todo = { id: string; text: string }
123
+
124
+ export const api = createApi({
125
+ reducerPath: 'api',
126
+ baseQuery: fetchBaseQuery({ baseUrl: '/api/' }),
127
+ endpoints: (build) => ({
128
+ getTodos: build.query<Todo[], void>({
129
+ query: () => 'todos',
130
+ }),
131
+ }),
132
+ })
133
+ ```
134
+
135
+ When the old code is just request status plus fetched data, migrate toward RTK Query instead of carrying the thunk stack forward forever.
136
+
137
+ ## Common Mistakes
138
+
139
+ ### HIGH Attempting a big-bang rewrite
140
+
141
+ Wrong:
142
+
143
+ ```ts
144
+ // Replace every reducer, every connected component, and every async flow
145
+ // in one branch before shipping anything.
146
+ ```
147
+
148
+ Correct:
149
+
150
+ ```ts
151
+ // 1) Switch createStore to configureStore
152
+ // 2) Migrate one touched reducer to createSlice
153
+ // 3) Convert touched connected components to hooks
154
+ // 4) Repeat without introducing new legacy Redux code
155
+ ```
156
+
157
+ Modern Redux migration is incremental, but once the store is modernized new work should stop adding legacy patterns.
158
+
159
+ Source: reduxjs/redux-toolkit:docs/usage/migrating-to-modern-redux.mdx
160
+
161
+ ### CRITICAL Carrying removed RTK 2 config forms forward
162
+
163
+ Wrong:
164
+
165
+ ```ts
166
+ configureStore({
167
+ reducer,
168
+ middleware: [logger],
169
+ })
170
+ ```
171
+
172
+ Correct:
173
+
174
+ ```ts
175
+ configureStore({
176
+ reducer,
177
+ middleware: (getDefaultMiddleware) =>
178
+ getDefaultMiddleware().concat(logger),
179
+ })
180
+ ```
181
+
182
+ RTK 2 removed array middleware configuration and other older builder forms that agents trained on RTK 1.x still emit.
183
+
184
+ Source: reduxjs/redux-toolkit:docs/usage/migrating-rtk-2.md
185
+
186
+ ### HIGH Preserving hand-written fetch state by default
187
+
188
+ Wrong:
189
+
190
+ ```ts
191
+ import { createAsyncThunk } from '@reduxjs/toolkit'
192
+
193
+ type Todo = { id: string; text: string }
194
+
195
+ const initialState = {
196
+ items: [] as Todo[],
197
+ status: 'idle' as 'idle' | 'pending' | 'failed',
198
+ }
199
+
200
+ export const fetchTodos = createAsyncThunk('todos/fetch', async () => {
201
+ const response = await fetch('/api/todos')
202
+ return (await response.json()) as Todo[]
203
+ })
204
+ ```
205
+
206
+ Correct:
207
+
208
+ ```ts
209
+ import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react'
210
+
211
+ type Todo = { id: string; text: string }
212
+
213
+ const api = createApi({
214
+ reducerPath: 'api',
215
+ baseQuery: fetchBaseQuery({ baseUrl: '/api/' }),
216
+ endpoints: (build) => ({
217
+ getTodos: build.query<Todo[], void>({
218
+ query: () => 'todos',
219
+ }),
220
+ }),
221
+ })
222
+ ```
223
+
224
+ If the feature is really server cache, keep the migration moving toward RTK Query instead of rebuilding the old loading-flag architecture in new APIs.
225
+
226
+ Source: reduxjs/redux-toolkit:docs/usage/migrating-to-modern-redux.mdx
@@ -0,0 +1,385 @@
1
+ ---
2
+ name: manage-server-data/adopt-rtk-query
3
+ description: >
4
+ Use this when adding RTK Query as the default server-data and document-cache
5
+ layer. Covers createApi, store integration, hooks, invalidation behavior,
6
+ optimistic updates, and deciding when RTK Query is the right cache model.
7
+ type: lifecycle
8
+ library: "@reduxjs/toolkit"
9
+ library_version: "2.11.2"
10
+ requires:
11
+ - build-modern-redux-apps/modern-redux
12
+ sources:
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
+ ---
20
+
21
+ # Adopt RTK Query
22
+
23
+ ## Setup
24
+
25
+ ```tsx
26
+ // file: src/services/api.ts
27
+ import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react'
28
+
29
+ type Post = { id: string; title: string }
30
+
31
+ export const api = createApi({
32
+ reducerPath: 'api',
33
+ baseQuery: fetchBaseQuery({ baseUrl: '/api/' }),
34
+ tagTypes: ['Post'],
35
+ endpoints: (build) => ({
36
+ getPosts: build.query<Post[], void>({
37
+ query: () => 'posts',
38
+ providesTags: (result) =>
39
+ result
40
+ ? [...result.map(({ id }) => ({ type: 'Post' as const, id })), 'Post']
41
+ : ['Post'],
42
+ }),
43
+ addPost: build.mutation<Post, Pick<Post, 'title'>>({
44
+ query: (body) => ({
45
+ url: 'posts',
46
+ method: 'POST',
47
+ body,
48
+ }),
49
+ invalidatesTags: ['Post'],
50
+ }),
51
+ }),
52
+ })
53
+
54
+ export const { useGetPostsQuery, useAddPostMutation } = api
55
+
56
+ // file: src/app/store.ts
57
+ import { configureStore } from '@reduxjs/toolkit'
58
+ import { api } from '../services/api'
59
+
60
+ export const store = configureStore({
61
+ reducer: {
62
+ [api.reducerPath]: api.reducer,
63
+ },
64
+ middleware: (getDefaultMiddleware) =>
65
+ getDefaultMiddleware().concat(api.middleware),
66
+ })
67
+
68
+ // file: src/App.tsx
69
+ import { Provider } from 'react-redux'
70
+ import { store } from './app/store'
71
+ import { useAddPostMutation, useGetPostsQuery } from './services/api'
72
+
73
+ function Posts() {
74
+ const { data: posts = [] } = useGetPostsQuery()
75
+ const [addPost] = useAddPostMutation()
76
+
77
+ return (
78
+ <div>
79
+ <button onClick={() => addPost({ title: 'Write docs' })}>Add</button>
80
+ <ul>
81
+ {posts.map((post) => (
82
+ <li key={post.id}>{post.title}</li>
83
+ ))}
84
+ </ul>
85
+ </div>
86
+ )
87
+ }
88
+
89
+ export function App() {
90
+ return (
91
+ <Provider store={store}>
92
+ <Posts />
93
+ </Provider>
94
+ )
95
+ }
96
+ ```
97
+
98
+ ## Core Patterns
99
+
100
+ ### Keep one API slice per base URL and extend it
101
+
102
+ ```ts
103
+ import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react'
104
+
105
+ export const api = createApi({
106
+ reducerPath: 'api',
107
+ baseQuery: fetchBaseQuery({ baseUrl: '/api/' }),
108
+ endpoints: () => ({}),
109
+ })
110
+
111
+ export const postsApi = api.injectEndpoints({
112
+ endpoints: (build) => ({
113
+ getPosts: build.query<{ id: string; title: string }[], void>({
114
+ query: () => 'posts',
115
+ }),
116
+ }),
117
+ })
118
+ ```
119
+
120
+ Split files with `injectEndpoints`, not by making multiple `createApi` roots for the same backend.
121
+
122
+ ### Use tags for cache invalidation
123
+
124
+ ```ts
125
+ type Post = { id: string; title: string }
126
+
127
+ export const api = createApi({
128
+ reducerPath: 'api',
129
+ baseQuery: fetchBaseQuery({ baseUrl: '/api/' }),
130
+ tagTypes: ['Post'],
131
+ endpoints: (build) => ({
132
+ getPosts: build.query<Post[], void>({
133
+ query: () => 'posts',
134
+ providesTags: (result) =>
135
+ result
136
+ ? [...result.map(({ id }) => ({ type: 'Post' as const, id })), 'Post']
137
+ : ['Post'],
138
+ }),
139
+ updatePost: build.mutation<Post, Pick<Post, 'id' | 'title'>>({
140
+ query: ({ id, title }) => ({
141
+ url: `posts/${id}`,
142
+ method: 'PATCH',
143
+ body: { title },
144
+ }),
145
+ invalidatesTags: (_result, _error, { id }) => [{ type: 'Post', id }],
146
+ }),
147
+ }),
148
+ })
149
+ ```
150
+
151
+ Treat tags as the normal invalidation path before reaching for manual cache patching.
152
+
153
+ ### Do optimistic updates in endpoint lifecycles
154
+
155
+ ```ts
156
+ type Post = { id: string; title: string }
157
+
158
+ export const api = createApi({
159
+ reducerPath: 'api',
160
+ baseQuery: fetchBaseQuery({ baseUrl: '/api/' }),
161
+ tagTypes: ['Post'],
162
+ endpoints: (build) => ({
163
+ getPosts: build.query<Post[], void>({
164
+ query: () => 'posts',
165
+ providesTags: ['Post'],
166
+ }),
167
+ updatePostTitle: build.mutation<Post, Pick<Post, 'id' | 'title'>>({
168
+ query: ({ id, title }) => ({
169
+ url: `posts/${id}`,
170
+ method: 'PATCH',
171
+ body: { title },
172
+ }),
173
+ async onQueryStarted({ id, title }, { dispatch, queryFulfilled }) {
174
+ const patch = dispatch(
175
+ api.util.updateQueryData('getPosts', undefined, (draft) => {
176
+ const post = draft.find((item) => item.id === id)
177
+ if (post) {
178
+ post.title = title
179
+ }
180
+ }),
181
+ )
182
+
183
+ try {
184
+ await queryFulfilled
185
+ } catch {
186
+ patch.undo()
187
+ }
188
+ },
189
+ }),
190
+ }),
191
+ })
192
+ ```
193
+
194
+ Keep optimistic and pessimistic cache updates inside endpoint lifecycle handlers so they stay coupled to the request.
195
+
196
+ ## Common Mistakes
197
+
198
+ ### CRITICAL Creating multiple API slices for one backend
199
+
200
+ Wrong:
201
+
202
+ ```ts
203
+ import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react'
204
+
205
+ type User = { id: string; name: string }
206
+
207
+ const baseQuery = fetchBaseQuery({ baseUrl: '/api/' })
208
+
209
+ const postsApi = createApi({
210
+ reducerPath: 'api',
211
+ baseQuery,
212
+ endpoints: () => ({}),
213
+ })
214
+
215
+ const usersApi = createApi({
216
+ reducerPath: 'api',
217
+ baseQuery,
218
+ endpoints: () => ({}),
219
+ })
220
+ ```
221
+
222
+ Correct:
223
+
224
+ ```ts
225
+ import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react'
226
+
227
+ type User = { id: string; name: string }
228
+
229
+ const baseQuery = fetchBaseQuery({ baseUrl: '/api/' })
230
+
231
+ const api = createApi({
232
+ reducerPath: 'api',
233
+ baseQuery,
234
+ endpoints: () => ({}),
235
+ })
236
+
237
+ const usersApi = api.injectEndpoints({
238
+ endpoints: (build) => ({
239
+ getUsers: build.query<User[], void>({ query: () => 'users' }),
240
+ }),
241
+ })
242
+ ```
243
+
244
+ One API slice per base URL preserves invalidation behavior and avoids duplicated middleware work.
245
+
246
+ Source: reduxjs/redux-toolkit:docs/rtk-query/api/createApi.mdx
247
+
248
+ ### HIGH Forgetting `api.reducer` or `api.middleware`
249
+
250
+ Wrong:
251
+
252
+ ```ts
253
+ import { configureStore } from '@reduxjs/toolkit'
254
+
255
+ const store = configureStore({
256
+ reducer: {},
257
+ })
258
+ ```
259
+
260
+ Correct:
261
+
262
+ ```ts
263
+ import { configureStore } from '@reduxjs/toolkit'
264
+
265
+ const store = configureStore({
266
+ reducer: {
267
+ [api.reducerPath]: api.reducer,
268
+ },
269
+ middleware: (getDefaultMiddleware) =>
270
+ getDefaultMiddleware().concat(api.middleware),
271
+ })
272
+ ```
273
+
274
+ RTK Query hooks need both the reducer and middleware to manage cache state and request lifecycles.
275
+
276
+ Source: reduxjs/redux-toolkit:docs/tutorials/rtk-query.mdx
277
+
278
+ ### MEDIUM Persisting browser API cache by default
279
+
280
+ Wrong:
281
+
282
+ ```ts
283
+ const storage = window.localStorage
284
+
285
+ const persistConfig = {
286
+ key: 'root',
287
+ storage,
288
+ }
289
+ ```
290
+
291
+ Correct:
292
+
293
+ ```ts
294
+ import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react'
295
+
296
+ const api = createApi({
297
+ reducerPath: 'api',
298
+ baseQuery: fetchBaseQuery({ baseUrl: '/api/' }),
299
+ endpoints: () => ({}),
300
+ })
301
+ ```
302
+
303
+ Persisting RTK Query cache in browsers often keeps stale data around longer than users expect; treat persistence as a special case, not the default.
304
+
305
+ Source: reduxjs/redux-toolkit:docs/rtk-query/usage/persistence-and-rehydration.mdx
306
+
307
+ ### HIGH Patching cache from components
308
+
309
+ Wrong:
310
+
311
+ ```tsx
312
+ import { useEffect } from 'react'
313
+ import { useAppDispatch } from '../../app/hooks'
314
+
315
+ const dispatch = useAppDispatch()
316
+
317
+ useEffect(() => {
318
+ dispatch(api.util.updateQueryData('getPosts', undefined, (draft) => {
319
+ draft.push({ id: 'p3', title: 'Patched from component' })
320
+ }))
321
+ }, [dispatch])
322
+ ```
323
+
324
+ Correct:
325
+
326
+ ```ts
327
+ updatePostTitle: build.mutation<Post, Pick<Post, 'id' | 'title'>>({
328
+ query: ({ id, title }) => ({
329
+ url: `posts/${id}`,
330
+ method: 'PATCH',
331
+ body: { title },
332
+ }),
333
+ async onQueryStarted({ id, title }, { dispatch, queryFulfilled }) {
334
+ const patch = dispatch(
335
+ api.util.updateQueryData('getPosts', undefined, (draft) => {
336
+ const post = draft.find((item) => item.id === id)
337
+ if (post) {
338
+ post.title = title
339
+ }
340
+ }),
341
+ )
342
+
343
+ try {
344
+ await queryFulfilled
345
+ } catch {
346
+ patch.undo()
347
+ }
348
+ },
349
+ })
350
+ ```
351
+
352
+ Component-level cache patches drift away from the mutation lifecycle that should own them.
353
+
354
+ Source: reduxjs/redux-toolkit:docs/rtk-query/usage/manual-cache-updates.mdx
355
+
356
+ ### HIGH Expecting invalidation to refetch unsubscribed queries
357
+
358
+ Wrong:
359
+
360
+ ```ts
361
+ import { api } from './api'
362
+ import { store } from './store'
363
+
364
+ const subscription = store.dispatch(api.endpoints.getPosts.initiate())
365
+ subscription.unsubscribe()
366
+ store.dispatch(api.util.invalidateTags(['Post']))
367
+ ```
368
+
369
+ Correct:
370
+
371
+ ```ts
372
+ import { api } from './api'
373
+ import { store } from './store'
374
+
375
+ store.dispatch(api.endpoints.getPosts.initiate())
376
+ store.dispatch(api.util.invalidateTags(['Post']))
377
+ ```
378
+
379
+ Invalidation only refetches actively subscribed queries; if no component is using that cache entry, RTK Query drops it and fetches again next time it is needed.
380
+
381
+ Source: reduxjs/redux-toolkit:docs/rtk-query/usage/automated-refetching.mdx
382
+
383
+ ## References
384
+
385
+ - [Endpoint lifecycle details and cache tradeoffs](references/endpoint-lifecycle.md)
@@ -0,0 +1,36 @@
1
+ # Endpoint Lifecycle
2
+
3
+ ## Invalidation rule
4
+
5
+ When a mutation invalidates tags:
6
+
7
+ - active subscribers refetch
8
+ - inactive cache entries are removed
9
+ - removed entries fetch again only when something subscribes later
10
+
11
+ That behavior is deliberate; invalidation is not a background "refresh everything" switch.
12
+
13
+ ## Document cache tradeoff
14
+
15
+ RTK Query is a document cache, not a normalized entity graph cache.
16
+
17
+ Use RTK Query by default when:
18
+
19
+ - the data comes from request/response APIs
20
+ - document caching is acceptable
21
+ - tag invalidation and endpoint lifecycles solve the problem
22
+
23
+ Reach for a different tool when:
24
+
25
+ - the real requirement is a normalized graph cache
26
+ - the stack already has a domain-specific normalized client that fits better
27
+
28
+ If normalized caching is mandatory and no better library is already in the stack, a slice plus thunk flow may be the fallback.
29
+
30
+ ## Useful endpoint options
31
+
32
+ - `providesTags`: tell RTK Query what cache entries this query represents
33
+ - `invalidatesTags`: tell RTK Query what a mutation dirties
34
+ - `onQueryStarted`: optimistic and pessimistic updates tied to a request
35
+ - `onCacheEntryAdded`: long-lived subscriptions such as streaming data
36
+ - `keepUnusedDataFor`: how long inactive cache entries stay around