Somehow I didn't find working example for the most basic case. I took example e.g. from here.
State:
interface AppUserState {
userId: string | undefined;
user: User;
status: 'idle' | 'pending' | 'succeeded' | 'failed';
error: Error | null;
}
I have a thunk:
export const fetchUser = createAsyncThunk(
'app-user/fetch-user',
async (id: string) => {
const response = await fetch(`/api/v1/user/id/${id}`);
return await response.json();
}
);
And a slice:
const usersSlice = createSlice({
name: 'app-user',
initialState,
reducers: {
setUserId(state, action: PayloadAction<string | undefined>) {
state.userId = action.payload;
}
},
extraReducers: (builder) => {
builder
.addCase(fetchUser.pending.type, (state) => {
state.status = 'pending';
})
.addCase(fetchUser.fulfilled.type, (state, action: PayloadAction<any>) => {
if (state.status === 'pending') {
state.status = 'succeeded';
state.user = {
id: action.payload.id,
name: action.payload.name,
type: action.payload.Type
};
}
})
.addCase(fetchUser.rejected.type, (state, action: PayloadAction<any>) => {
if (state.status === 'pending') {
state.status = 'failed';
state.error = action.payload;
}
})
}
});
This doesn't work. When there's no user and fetch returns 404 rejected case is not handled.
FIX:
If I change the fetch to code below the rejected case is handled but the payload doesn't contain the error I pass to reject.
export const fetchUser = createAsyncThunk(
'app-user/fetch-user',
async (id: string) => {
const response = await fetch(`/api/v1/user/id/${id}`);
if (!response.ok) {
return Promise.reject(new Error(response.statusText));
}
return await response.json();
}
);
Instead the error is directly in action. This can be fixed by changing the rejected case:
.addCase(fetchUser.rejected.type, (state, action) => {
if (state.status === 'pending') {
state.status = 'failed';
if ('error' in action) {
state.error = action.error as Error;
}
}
})
But this seems not to be the standard way to do this. There's lots of examples that are not for fetch API. So how this should be done?
fetchdoes not reject on status 404. See Checking that the fetch was successful for details.It would seem you are about halfway there to processing the fetched data. I would suggest just surrounding all the asynchronous code/logic in a
try/catch, assume the "happy path", and if there are any errors/rejections along the way then return the error value withrejectWithValueinstead of throwing another error or returning aPromise.reject. This places the "error" (whatever it is) on the*.rejectedaction'spayload.Example:
Note: that you may need to adjust the
AppUserStateinterface to match anything you are rejecting in the thunk and setting into state.*