React 18中外部重定向后组件重复渲染及dispatch执行两次问题排查
React 18 OAuth回调组件重复渲染导致Dispatch执行两次的问题
从外部站点重定向到OAuth回调页面后,Oauth组件出现重复渲染,即使使用useRef处理React 18 Strict Mode下useEffect的二次执行特性,dispatch(getOauth(...))仍然执行两次。即使清空组件内仅保留日志,重复渲染问题依然存在。
路由代码
return ( <Grid> <PersistGate loading={null} persistor={persistor}> <SidebarProvider> <Routes> <Route path="/" element={<Welcome />} /> <Route path="oauth2-callback" element={<Oauth />} /> <Route element={<ProtectedRoutes />}> <Route element={<Layout />}> <Route path="/home" element={<Home />} /> <Route path="/client-view/*" element={<Submission />} /> </Route> </Route> </Routes> </SidebarProvider> </PersistGate> </Grid> ); }; export default App;
组件代码
import { useSearchParams } from "react-router-dom"; import { useAppDispatch } from "../../store/hooks/hooks"; import { getOauth } from "../../store/thunks/app/app.thunks"; const Oauth = () => { const dispatch = useAppDispatch(); const shouldDispatch = useRef(true); const [searchParams] = useSearchParams(); useEffect(() => { if (shouldDispatch.current) { shouldDispatch.current = false; dispatch( getOauth({ code: searchParams.get("code") || "", state: searchParams.get("state") || "", }) ); } }, []); return null; }; export default Oauth;
回调URL
https://localhost:3000/oauth2-callback?state=dsada2321&code=12345
解决方案
1. 排查Redux Persist的状态恢复影响
Redux Persist的PersistGate在恢复持久化状态时,可能会触发组件树的重新渲染。可以尝试:
- 检查
persistor配置,确保状态恢复逻辑没有重复触发页面更新 - 在
PersistGate的onBeforeLift回调中添加逻辑,等待状态完全恢复后再处理OAuth回调
2. 适配React 18 Strict Mode的双重挂载
React 18开发环境下的Strict Mode会对组件进行双重挂载,这是框架默认用于检测副作用的行为。如果临时关闭Strict Mode后问题消失,说明是该特性导致:
- 不要长期关闭Strict Mode,而是调整逻辑:可以将
shouldDispatch的判断逻辑更严谨,或者直接在生产环境验证(生产环境不会触发双重挂载)
3. 调整useEffect的依赖项
当前useEffect依赖项为空数组,但searchParams是动态实例,组件重新挂载时可能会变化。将searchParams和dispatch加入依赖项,结合useRef控制执行:
useEffect(() => { if (shouldDispatch.current) { shouldDispatch.current = false; dispatch( getOauth({ code: searchParams.get("code") || "", state: searchParams.get("state") || "", }) ); } }, [searchParams, dispatch]);
4. 使用useLayoutEffect替代useEffect
useLayoutEffect会在DOM更新前同步执行,可能避免异步触发的重复渲染:
useLayoutEffect(() => { if (shouldDispatch.current) { shouldDispatch.current = false; dispatch( getOauth({ code: searchParams.get("code") || "", state: searchParams.get("state") || "", }) ); } }, [searchParams, dispatch]);
5. 检查OAuth重定向流程
确认外部站点的重定向是否仅触发一次,是否存在多重重定向(比如先到中间页再跳转回调页),导致组件多次挂载。
内容的提问来源于stack exchange,提问作者Neketo
相关产品推荐
相关产品推荐

