Skip to content

Commit fc6a8b8

Browse files
msutkowskiphryneas
authored andcommitted
Expand on rejectWithValue and type references
1 parent c99d977 commit fc6a8b8

File tree

1 file changed

+51
-2
lines changed

1 file changed

+51
-2
lines changed

docs/usage/usage-with-typescript.md

Lines changed: 51 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -451,14 +451,15 @@ If you are performing a request that you know will typically either be a success
451451

452452
```ts
453453
interface MyKnownError {
454+
errorMessage: string
454455
// ...
455456
}
456457

457458
const updateUserById = createAsyncThunk<
458459
// Return type of the payload creator
459460
MyData,
460461
// First argument to the payload creator
461-
number,
462+
{ id: string; first_name: string; last_name: string; email: string },
462463
// Types for ThunkAPI
463464
{
464465
extra: {
@@ -476,7 +477,7 @@ const updateUserById = createAsyncThunk<
476477
body: JSON.stringify(userData)
477478
})
478479
if (response.status === 400) {
479-
// Return any known error for future handling
480+
// Return the known error for future handling
480481
return thunkApi.rejectWithValue((await response.json()) as MyKnownError)
481482
}
482483
return (await response.json()) as MyData
@@ -485,6 +486,54 @@ const updateUserById = createAsyncThunk<
485486

486487
While this notation for `state`, `dispatch`, `extra` and `rejectValue` might seem uncommon at first, it allows you to provide only the types for these you actually need - so for example, if you are not accessing `getState` within your `payloadCreator`, there is no need to provide a type for `state`. The same can be said about `rejectValue` - if you don't need to access any potential error payload, you can ignore it.
487488

489+
In addition, you can leverage checks against `action.payload` and `match` as provided by `createAction` as a type-guard for when you want to access known properties on defined types. Example:
490+
491+
- In a reducer
492+
493+
```ts
494+
const usersSlice = createSlice({
495+
name: 'users',
496+
initialState: {
497+
entities: {},
498+
error: null
499+
},
500+
reducers: {},
501+
extraReducers: builder => {
502+
builder.addCase(updateUserById.fulfilled, (state, { payload }) => {
503+
state.entities[payload.id] = payload
504+
})
505+
builder.addCase(updateUserById.rejected, (state, action) => {
506+
if (action.payload) {
507+
// Being that we passed in MyKnownError to rejectType in `updateUserById`, the type information will be available here.
508+
state.error = action.payload.errorMessage
509+
} else {
510+
state.error = action.error
511+
}
512+
})
513+
}
514+
})
515+
```
516+
517+
- In a component
518+
519+
```ts
520+
const updateUser = async userData => {
521+
const { id, ...userFields } = userData
522+
const resultAction = await dispatch(updateUserById(userFields))
523+
if (updateUser.fulfilled.match(resultAction)) {
524+
const user = unwrapResult(resultAction)
525+
showToast('success', `Updated ${user.name}`)
526+
} else {
527+
if (resultAction.payload) {
528+
// Being that we passed in MyKnownError to rejectType in `updateUserById`, the type information will be available here.
529+
showToast('error', `Update failed: ${rersultAction.payload.errorMessage}`)
530+
} else {
531+
showToast('error', `Update failed: ${resultAction.error.message}`)
532+
}
533+
}
534+
}
535+
```
536+
488537
## `createEntityAdapter`
489538

490539
Typing `createEntityAdapter` only requires you to specify the entity type as the single generic argument.

0 commit comments

Comments
 (0)