React StrictMode下Redux拉取数据时action重复触发问题
问题成因
该问题是React 18+版本<React.StrictMode>开发环境的预期行为,和Redux本身无关:
- 开发模式下StrictMode会主动执行两次组件挂载-卸载流程,用来提前检测代码中不规范的副作用逻辑,比如缺失清理逻辑的
useEffect、不安全的生命周期调用等。生产环境下StrictMode不会触发该双重执行逻辑,因此移除StrictMode包裹后功能暂时恢复正常,但这属于规避问题而非修复。 - 你当前写在
useEffect中的数据请求逻辑没有做请求中止、竞态处理、重复触发拦截,组件双重挂载时effect会执行两次,对应重复发起请求、重复dispatch action,最终出现请求重复发、数据重复追加到store的问题。
正确修复方案
不要通过移除StrictMode解决问题,只需要补全副作用的规范处理逻辑即可:
- 给异步请求增加AbortController中止控制,在
useEffect的清理函数中中止未完成的上一次请求,避免组件重挂载时的无效请求和过期dispatch - 增加加载状态锁,请求发起前判断当前是否已经处于加载中,是则直接拦截不重复触发
- 针对加载更多的追加场景,增加游标参数和数据去重逻辑,避免硬编码拉取参数导致的重复数据问题
修复后的App组件参考代码:
function App() { const dispatch = useDispatch(); const data = useSelector(questionsData); // 从store中取出loading状态做请求锁 const loading = useSelector(state => state.questionsStore.loading); useEffect(() => { const abortController = new AbortController(); const fetchQuestions = async () => { // 加载中直接拦截,不重复发请求 if (loading) return; dispatch(fetchQuestionsBegin()); try { // 把abort信号传给请求实例,支持主动中止 const { data } = await mainUrl(`/questions?last=5`, { signal: abortController.signal }); // 确认请求未被中止再dispatch结果 if (!abortController.signal.aborted) { dispatch(fetchQuestionsSuccess(data)); } } catch (err) { // 过滤主动中止的请求错误,只处理真实请求失败 if (!abortController.signal.aborted) { dispatch(fetchQuestionsFailure(err?.message || '请求失败')); } } }; fetchQuestions(); // effect清理函数:组件重执行/卸载前中止上一次未完成的请求 return () => abortController.abort(); }, [dispatch, loading]); return ( <> <div>TEST</div> </> ); }
如果项目中异步请求逻辑较多,推荐直接使用Redux Toolkit官方的createAsyncThunk工具编写异步逻辑,它内置了竞态处理、请求中止、状态自动管理的能力,不需要手动在组件中处理重复触发问题,代码稳定性更高。
注意:StrictMode的双重执行是React提供的代码隐患检测机制,修复副作用清理逻辑后,不管是开发环境的双重挂载、还是生产环境中组件意外重挂载、网络请求延迟导致的竞态问题,都不会再出现action重复触发、数据重复追加的问题。
内容的提问来源于stack exchange,提问作者Erol
相关产品推荐
相关产品推荐

