Redux Thunk结合Fetch API的错误处理标准实现方案
关于Redux Toolkit中createAsyncThunk结合Fetch API的标准错误处理方式
我找不到Redux Thunk结合Fetch API做错误处理的基础可用示例,参考教程实现后,请求返回404时无法触发rejected分支。修改Fetch逻辑后虽能触发分支,但错误信息的获取方式不符合预期,想了解标准实现方式。
状态定义
interface AppUserState { userId: string | undefined; user: User; status: 'idle' | 'pending' | 'succeeded' | 'failed'; error: Error | null; }
初始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(); } );
初始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; } }) } });
核心问题
请求返回404时rejected分支未触发——因为Fetch API仅在网络故障时才会抛出Promise拒绝,HTTP错误状态码(如404、500)不会自动触发reject。
尝试修复后的代码
修改后的Thunk函数
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(); } );
修改后的rejected分支处理
.addCase(fetchUser.rejected.type, (state, action) => { if (state.status === 'pending') { state.status = 'failed'; if ('error' in action) { state.error = action.error as Error; } } })
但这种从action.error取错误的方式,感觉不是标准实现,想知道正确的处理姿势。
标准实现方式
1. Thunk函数规范抛出错误
使用Redux Toolkit提供的rejectWithValue方法传递错误信息,这是官方推荐的错误payload传递方式,能让错误信息规整地出现在action.payload中:
export const fetchUser = createAsyncThunk( 'app-user/fetch-user', async (id: string, { rejectWithValue }) => { try { const response = await fetch(`/api/v1/user/id/${id}`); if (!response.ok) { // 尝试解析服务器返回的结构化错误信息,解析失败则用状态文本兜底 const errorData = await response.json().catch(() => ({ message: response.statusText })); // 传递包含状态码和错误信息的自定义payload return rejectWithValue({ status: response.status, message: errorData.message || '请求失败' }); } return await response.json(); } catch (err) { // 捕获网络层面的错误(如断网) return rejectWithValue({ status: 0, message: err instanceof Error ? err.message : '网络请求失败' }); } } );
2. Slice中规范处理rejected分支
先定义错误类型,再直接从action.payload获取错误信息,类型更安全:
// 定义错误payload的类型 interface FetchError { status: number; message: string; } // 修改Slice的extraReducers部分 const usersSlice = createSlice({ // ...其他配置不变 extraReducers: (builder) => { builder // 注意:不需要再加.type,直接用fetchUser.pending/fulfilled/rejected即可 .addCase(fetchUser.pending, (state) => { state.status = 'pending'; }) .addCase(fetchUser.fulfilled, (state, action) => { if (state.status === 'pending') { state.status = 'succeeded'; state.user = { id: action.payload.id, name: action.payload.name, type: action.payload.Type }; } }) .addCase(fetchUser.rejected, (state, action: PayloadAction<FetchError | undefined>) => { if (state.status === 'pending') { state.status = 'failed'; state.error = action.payload ? new Error(`${action.payload.status}: ${action.payload.message}`) : new Error('未知错误'); } }); } });
3. 关键说明
- Fetch API的特性:必须手动检查
response.ok(状态码200-299时为true),否则HTTP错误不会触发Promise拒绝。 rejectWithValue是官方推荐的错误传递方式,相比直接Promise.reject,能让错误信息更规范地挂载到action.payload,而非仅依赖action.error。- 可以根据业务需求,在错误payload中携带更多服务器返回的详情,方便组件展示更精准的错误提示。
内容的提问来源于stack exchange,提问作者char m
相关产品推荐
相关产品推荐

