Skip to content

Commit b972fcd

Browse files
committed
draft docs
1 parent a15d6e8 commit b972fcd

File tree

3 files changed

+192
-9
lines changed

3 files changed

+192
-9
lines changed

docs/api/createSlice.mdx

Lines changed: 190 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,196 @@ const todosSlice = createSlice({
130130
})
131131
```
132132

133+
### The `reducers` "creator callback" notation
134+
135+
Alternatively, the `reducers` field can be a callback which receives a "create" object.
136+
137+
The main benefit of this is that you can create [async thunks](./createAsyncThunk) as part of your slice. Types are also slightly simplified for prepared reducers.
138+
139+
```ts title="Creator callback for reducers"
140+
import { createSlice, nanoid } from '@reduxjs/toolkit'
141+
import type { PayloadAction } from '@reduxjs/toolkit'
142+
143+
interface Item {
144+
id: string
145+
text: string
146+
}
147+
148+
interface TodoState {
149+
loading: boolean
150+
todos: Item[]
151+
}
152+
153+
const todosSlice = createSlice({
154+
name: 'todos',
155+
initialState: {
156+
loading: false,
157+
todos: [],
158+
} as TodoState,
159+
reducers: (create) => ({
160+
deleteTodo: create.reducer((state, action: PayloadAction<number>) => {
161+
state.todos.splice(action.payload, 1)
162+
}),
163+
addTodo: create.preparedReducer(
164+
(text: string) => {
165+
const id = nanoid()
166+
return { payload: { id, text } }
167+
},
168+
// action type is inferred from prepare callback
169+
(state, action) => {
170+
state.todos.push(action.payload)
171+
}
172+
),
173+
fetchTodo: create.asyncThunk(
174+
async (id: string, thunkApi) => {
175+
const res = await fetch(`myApi/todos?id=${id}`)
176+
return (await res.json()) as Item
177+
},
178+
{
179+
pending: (state) => {
180+
state.loading = true
181+
},
182+
rejected: (state, action) => {
183+
state.loading = false
184+
},
185+
fulfilled: (state, action) => {
186+
state.loading = false
187+
state.todos.push(action.payload)
188+
},
189+
}
190+
),
191+
}),
192+
})
193+
194+
export const { addTodo, deleteTodo, fetchTodo } = todosSlice.actions
195+
```
196+
197+
#### Create Methods
198+
199+
#### `create.reducer`
200+
201+
A standard slice case reducer.
202+
203+
**Parameters**
204+
205+
- **reducer** The slice case reducer to use.
206+
207+
```ts no-transpile
208+
create.reducer((state, action: PayloadAction<Todo>) => {
209+
state.todos.push(action.payload)
210+
})
211+
```
212+
213+
#### `create.preparedReducer`
214+
215+
A [prepared](#customizing-generated-action-creators) reducer, to customize the action creator.
216+
217+
**Parameters**
218+
219+
- **prepareAction** The [`prepare callback`](./createAction#using-prepare-callbacks-to-customize-action-contents).
220+
- **reducer** The slice case reducer to use.
221+
222+
The action passed to the case reducer will be inferred from the prepare callback's return.
223+
224+
```ts no-transpile
225+
create.preparedReducer(
226+
(text: string) => {
227+
const id = nanoid()
228+
return { payload: { id, text } }
229+
},
230+
(state, action) => {
231+
state.todos.push(action.payload)
232+
}
233+
)
234+
```
235+
236+
#### `create.asyncThunk`
237+
238+
Creates an async thunk instead of an action creator.
239+
240+
**Parameters**
241+
242+
- **payloadCreator** The thunk [payload creator](./createAsyncThunk#payloadcreator).
243+
- **config** The configuration object. (optional)
244+
245+
The configuration object can contain case reducers for each of the [lifecycle actions](./createAsyncThunk#promise-lifecycle-actions) (`pending`, `fulfilled`, and `rejected`).
246+
247+
Each case reducer will be attached to the slice's `caseReducers` object, e.g. `slice.caseReducers.fetchTodo.fulfilled`.
248+
249+
The configuration object can also contain [`options`](./createAsyncThunk#options).
250+
251+
```ts no-transpile
252+
create.asyncThunk(
253+
async (id: string, thunkApi) => {
254+
const res = await fetch(`myApi/todos?id=${id}`)
255+
return (await res.json()) as Item
256+
},
257+
{
258+
pending: (state) => {
259+
state.loading = true
260+
},
261+
rejected: (state, action) => {
262+
state.loading = false
263+
},
264+
fulfilled: (state, action) => {
265+
state.loading = false
266+
state.todos.push(action.payload)
267+
},
268+
options: {
269+
idGenerator: uuid,
270+
},
271+
}
272+
)
273+
```
274+
275+
:::note
276+
277+
Typing for the `create.asyncThunk` works in the same way as [`createAsyncThunk`](usage/usage-with-typescript#createasyncthunk), with one key difference.
278+
279+
A type for `state` and/or `dispatch` _cannot_ be provided as part of the `ThunkApiConfig`, as this would cause circular types.
280+
281+
Instead, it is necessary to assert the type when needed.
282+
283+
```ts no-transpile
284+
create.asyncThunk<Todo, string, { rejectValue: { error: string } }>(
285+
async (id, thunkApi) => {
286+
const state = thunkApi.getState() as RootState
287+
const dispatch = thunkApi.dispatch as AppDispatch
288+
throw thunkApi.rejectWithValue({
289+
error: 'Oh no!',
290+
})
291+
}
292+
)
293+
```
294+
295+
For common thunk API configuration options, a [`withTypes` helper](usage/usage-with-typescript#defining-a-pre-typed-createasyncthunk) is provided:
296+
297+
```ts no-transpile
298+
reducers: (create) => {
299+
const createAThunk =
300+
create.asyncThunk.withTypes<{ rejectValue: { error: string } }>()
301+
302+
return {
303+
fetchTodo: createAThunk<Todo, string>(async (id, thunkApi) => {
304+
const state = thunkApi.getState() as RootState
305+
const dispatch = thunkApi.dispatch as AppDispatch
306+
throw thunkApi.rejectWithValue({
307+
error: 'Oh no!',
308+
})
309+
}),
310+
fetchTodos: createAThunk<Todo[], string>(async (id, thunkApi) => {
311+
const state = thunkApi.getState() as RootState
312+
const dispatch = thunkApi.dispatch as AppDispatch
313+
throw thunkApi.rejectWithValue({
314+
error: 'Oh no, not again!',
315+
})
316+
}),
317+
}
318+
}
319+
```
320+
321+
:::
322+
133323
### `extraReducers`
134324

135325
One of the key concepts of Redux is that each slice reducer "owns" its slice of state, and that many slice reducers

docs/api/getDefaultMiddleware.mdx

Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -40,14 +40,7 @@ to the store. `configureStore` will not add any extra middleware beyond what you
4040
`getDefaultMiddleware` is useful if you want to add some custom middleware, but also still want to have the default
4141
middleware added as well:
4242

43-
```ts
44-
// file: reducer.ts noEmit
45-
46-
export default function rootReducer(state = {}, action: any) {
47-
return state
48-
}
49-
50-
// file: store.ts
43+
```ts no-transpile
5144
import { configureStore } from '@reduxjs/toolkit'
5245

5346
import logger from 'redux-logger'

docs/rtk-query/usage/error-handling.mdx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@ Redux Toolkit has [action matching utilities](../../api/matching-utilities.mdx#m
8080

8181
:::
8282

83-
```ts title="Error catching middleware example"
83+
```ts no-transpile title="Error catching middleware example"
8484
import { isRejectedWithValue } from '@reduxjs/toolkit'
8585
import type { MiddlewareAPI, Middleware } from '@reduxjs/toolkit'
8686
import { toast } from 'your-cool-library'

0 commit comments

Comments
 (0)