Redux Toolkit中Action无法正常触发的原因及解决方法
问题描述
我编写了如下studentSlice:
const studentSlice = createSlice({ name: 'studentSlice', initialState: { data: {}, loading: false, error: null, }, reducers: { setData: (state, action) => { state.data = action.payload; }, setLoading: (state, action) => { state.loading = action.payload; }, setError: (state, action) => { state.error = action.payload; }, }, }); export const { setData, setLoading, setError } = studentSlice.actions; export default studentSlice.reducer;
尝试创建调用多个reducer的异步action:
export const getStudentData = (): any => async (dispatch: any) => { try { console.log("here"); dispatch(setLoading(true)); const { data, isLoading, error } = await personApi.endpoints.getStudentList.useQueryState(); // Using RTK Query console.log(data); if (isLoading) { return; } if (error) { throw error; } dispatch(setData(data)); dispatch(setLoading(false)); } catch (error) { dispatch(setError(error)); } };
在组件中通过useEffect触发该action:
const StudentProfile = () => { const dispatch = useDispatch(); useEffect(() => { dispatch(getStudentData()) }, []) const students = useSelector((state: any) => state.student.data)) return ... // some components that use this students }
但getStudentData完全没执行(没打印日志),同时之前尝试用extraReducers同步RTK Query结果到slice也没效果。我的API slice代码如下:
const personApi = api.injectEndpoints({ endpoints: (builder) => ({ getStudentList: builder.query<StudentsResult, void>({ query: () => ({ url: 'students', method: 'POST', body: JSON.stringify({ // some fields }), }), providesTags: ['Students'], }), }), });
求问原因和正确触发方式。
原因分析
- Hook规则违规:
useQueryState是RTK Query提供的React Hook,必须在组件或自定义Hook的顶层调用,不能放在异步action这类普通函数里。这种违规会直接导致函数执行中断,甚至抛出错误(可能被错误边界吞掉),所以console.log("here")都没输出。 - 依赖数组不规范:
useEffect的依赖数组为空,虽然dispatch在多数情况下稳定,但严格模式下建议把dispatch加入依赖数组,避免潜在的执行异常。
正确解决方案
方案一:直接使用RTK Query Hook(推荐)
RTK Query本身已经封装了data、isLoading、error等状态,无需手动维护slice,直接在组件中调用自动生成的Hook即可:
const StudentProfile = () => { const { data: students, isLoading, error } = personApi.useGetStudentListQuery(); // 处理加载/错误状态 if (isLoading) return <div>加载中...</div>; if (error) return <div>加载失败:{(error as Error).message}</div>; return ( // 渲染学生列表的组件 ) }
方案二:通过extraReducers同步到自定义slice
如果一定要将RTK Query的结果同步到自己的slice,需要用extraReducers监听RTK Query的action类型,无需自定义异步action:
// 修改studentSlice添加extraReducers const studentSlice = createSlice({ name: 'studentSlice', initialState: { data: {}, loading: false, error: null, }, reducers: { setData: (state, action) => { state.data = action.payload; }, setLoading: (state, action) => { state.loading = action.payload; }, setError: (state, action) => { state.error = action.payload; }, }, extraReducers: (builder) => { builder // 查询开始时更新状态 .addMatcher(personApi.endpoints.getStudentList.matchPending, (state) => { state.loading = true; state.error = null; }) // 查询成功时更新数据 .addMatcher(personApi.endpoints.getStudentList.matchFulfilled, (state, action) => { state.loading = false; state.data = action.payload; }) // 查询失败时记录错误 .addMatcher(personApi.endpoints.getStudentList.matchRejected, (state, action) => { state.loading = false; state.error = action.error; }); }, }); // 组件中触发查询 const StudentProfile = () => { const dispatch = useDispatch(); useEffect(() => { // 用initiate触发查询,返回的promise可用于取消请求 const queryPromise = dispatch(personApi.endpoints.getStudentList.initiate()); return () => queryPromise.unsubscribe(); }, [dispatch]) const { data: students, loading, error } = useSelector((state: any) => state.student); if (loading) return <div>加载中...</div>; if (error) return <div>加载失败:{(error as Error).message}</div>; return ( // 渲染学生列表的组件 ) }
方案三:修复自定义异步action(不推荐)
如果坚持要用自定义异步action,不能用useQueryState,改用initiate方法触发查询并获取结果:
export const getStudentData = () => async (dispatch: any) => { try { console.log("here"); dispatch(setLoading(true)); // 用initiate触发查询并通过unwrap获取结果 const data = await dispatch(personApi.endpoints.getStudentList.initiate()).unwrap(); dispatch(setData(data)); dispatch(setLoading(false)); } catch (error) { dispatch(setError(error)); dispatch(setLoading(false)); } }; // 组件中使用 const StudentProfile = () => { const dispatch = useDispatch(); useEffect(() => { dispatch(getStudentData()); }, [dispatch]) const { data: students, loading, error } = useSelector((state: any) => state.student); if (loading) return <div>加载中...</div>; if (error) return <div>加载失败:{(error as Error).message}</div>; return ( // 渲染学生列表的组件 ) }
内容的提问来源于stack exchange,提问作者sempraEdic
相关产品推荐
相关产品推荐

