React useEffect中如何判断Redux-Toolkit异步Action的Pending状态?
解决Redux-Toolkit Async Thunk重复触发问题
问题场景
我用Redux-Toolkit创建了如下async thunk:
export const getMe = createAsyncThunk(`${SLICE_NAME}/me`, async () => { return await fetchMe(); });
并在React的useEffect中分发该Action:
useEffect(() => { if (!token) return; if (user) return; dispatch(getMe()); }, [dispatch, token, user]);
但getMe Action被重复分发两次,已知是useEffect依赖项导致的触发时机问题。尝试用本地状态控制:
useEffect(() => { if (!token) return; if (user) return; if (isLoading) return; setIsLoading(true); dispatch(getMe()).finally(() => { setIsLoading(false) }); }, [dispatch, token, user]);
但问题依旧,getMe仍会被近乎同时触发两次。希望像判断请求成功状态那样,通过Redux-Toolkit自带的方式判断Action是否处于pending状态:
const res = await dispatch(getMe()); if (getMe.fulfilled.match(res)) { // api call was successful }
解决方案
核心是用Redux Store统一管理Async Thunk的pending状态,替代本地组件状态——本地状态更新存在异步延迟,赶不上useEffect的两次触发时机,而Redux状态更新是同步的,能即时生效。
1. 在Slice中追踪Async Thunk的请求状态
在你的slice定义里,通过extraReducers监听getMe的三个状态(pending/fulfilled/rejected),并维护对应的状态字段:
const userSlice = createSlice({ name: SLICE_NAME, initialState: { user: null, // 新增:专门追踪getMe的请求状态 isGetMePending: false }, reducers: {}, extraReducers: (builder) => { builder .addCase(getMe.pending, (state) => { state.isGetMePending = true; }) .addCase(getMe.fulfilled, (state, action) => { state.isGetMePending = false; state.user = action.payload; }) .addCase(getMe.rejected, (state) => { state.isGetMePending = false; }); } });
2. 在组件中使用Store中的pending状态
通过useSelector从Store中取出isGetMePending,并加入useEffect的依赖和判断逻辑:
import { useSelector, useDispatch } from 'react-redux'; import { getMe } from './your-slice-path'; function YourComponent() { const dispatch = useDispatch(); const token = useSelector(state => state.auth.token); // 假设token存于auth slice const user = useSelector(state => state.user.user); // 假设user存于user slice // 获取RTK管理的pending状态 const isGetMePending = useSelector(state => state.user.isGetMePending); useEffect(() => { if (!token) return; if (user) return; // 用Store中的pending状态替代本地isLoading if (isGetMePending) return; dispatch(getMe()); }, [dispatch, token, user, isGetMePending]); // 组件其他逻辑... }
为什么这能解决问题?
- Redux Store的状态更新是同步的:第一次
dispatch(getMe())触发后,isGetMePending会立刻变为true,第二次useEffect触发时这个状态已经生效,直接跳过重复请求。 - 本地状态
isLoading的更新是异步的:第一次useEffect执行时setIsLoading(true)还未完成状态更新,第二次useEffect触发时isLoading仍为初始值false,导致重复dispatch。
内容的提问来源于stack exchange,提问作者Vishal
相关产品推荐
相关产品推荐

