React中useEffect触发两次,Redux获取后端数据如何仅执行一次?
解决React useEffect执行两次导致重复请求的问题
原因分析
在React 18的开发模式下,StrictMode会刻意让组件挂载→卸载→重新挂载,以此检测组件中是否存在未正确清理的副作用。你的useEffect在组件挂载时触发,所以会执行两次,这是开发环境的正常行为,但生产环境不会出现该情况。
解决方案
方案1:临时关闭StrictMode(不推荐长期使用)
找到项目入口文件(通常是index.js),移除<StrictMode>的包裹:
// 原代码 ReactDOM.createRoot(document.getElementById('root')).render( <StrictMode> <App /> </StrictMode> ); // 修改后 ReactDOM.createRoot(document.getElementById('root')).render( <App /> );
这种方式能快速让useEffect只执行一次,但会失去StrictMode的副作用检测能力,不利于排查潜在问题。
方案2:给副作用添加清理逻辑,避免重复请求
使用AbortController取消重复请求,确保即使useEffect执行两次,也只会有一次有效请求:
function App() { const dispatch = useDispatch(); useEffect(() => { const controller = new AbortController(); const fetchPosts = async () => { try { const response = await fetch('/api/posts', { signal: controller.signal }); const data = await response.json(); dispatch(getPosts(data)); } catch (error) { if (error.name !== 'AbortError') { console.error('获取帖子失败:', error); } } }; fetchPosts(); // 组件卸载时取消请求 return () => controller.abort(); }, [dispatch]); const posts = useSelector((state) => state.posts); console.log(posts); return ( <div> <h1>App</h1> </div> ); }
如果你的getPosts是Redux Toolkit的createAsyncThunk,它本身支持AbortController,可以直接传递signal:
// 定义action时 export const getPosts = createAsyncThunk('posts/getPosts', async (_, { signal }) => { const response = await fetch('/api/posts', { signal }); return response.json(); }); // 组件中使用 useEffect(() => { const promise = dispatch(getPosts()); return () => promise.abort(); }, [dispatch]);
方案3:确保Redux action是幂等的
让getPosts action在处理时,即使被dispatch多次,也不会重复更新store或发送重复请求。比如在后端接口做幂等处理,或者在Redux reducer中判断数据是否已存在,避免重复更新。
注意
生产环境下React不会触发StrictMode的重复挂载行为,上线后不会出现两次请求的问题。开发阶段更推荐使用方案2或3,既能保留StrictMode的检测能力,又能避免无效的重复请求。
内容的提问来源于stack exchange,提问作者Prajwal Raj
相关产品推荐
相关产品推荐

