React.js移除StrictMode后组件仍两次渲染的原因排查
React 组件两次渲染原因说明
你现在看到的两次渲染是React的正常执行逻辑,不是异常bug,执行流程如下:
- 第一次渲染:组件首次挂载,执行组件顶层所有代码,打印初始
memberArea值0,挂载完成后执行useEffect中注册的getData接口请求 - 第二次渲染:接口返回数据后调用
setMemberArray更新状态,状态变更触发组件重渲染,再次执行顶层代码打印memberArea值,这个流程和React严格模式无关,只要你调用状态更新方法就会触发重渲染
不要把正常的状态更新重渲染当成问题,React本身就是靠状态变化触发重渲染来更新视图的,只要没有出现渲染死循环、渲染次数远超预期的情况,不需要强行消除重渲染。
现有代码的潜在问题(可能触发更多次非预期渲染)
useEffect依赖项包含memberArea,只要memberArea值发生变动,就会重新触发接口请求,请求返回后更新状态再次触发重渲染。如果其他逻辑里意外修改了memberArea(哪怕重复设置相同的0值,React17及更早版本不会做优化跳过渲染),就会出现额外渲染。getData定义在组件内部,每次渲染都会生成新的函数引用,如果后续把getData加入其他Hook的依赖数组,会意外触发依赖更新,导致多余执行。- 你贴的代码里
memberNum没有定义来源,如果这个值是从props或其他状态派生的,取值逻辑不对也可能触发重复请求。 - 没有做组件卸载判断,如果请求返回前组件已经卸载,调用
setMemberArray会触发内存泄漏警告。
优化后参考代码
// 不依赖组件内部状态的请求逻辑移到组件外,避免重复生成函数引用 const fetchMemberData = async (memberCode: string) => { try { const res = await axios.get(`api/v1/member?memberCode=${memberCode}`); return res.data.data.memberList; } catch (err) { console.log(err); return []; } }; // 组件内部逻辑 const YourComponent = () => { const [memberArray, setMemberArray] = useState<any[]>([]); const [memberArea, setMemberArea] = useState(0); // 替换成你实际的memberNum来源,比如从props、路由参数、其他状态获取 const memberNum = ''; console.log("memberArea: " + memberArea); useEffect(() => { let isUnmounted = false; fetchMemberData(memberNum).then(list => { if (!isUnmounted) { setMemberArray([...list]); } }); // 卸载时标记,避免内存泄漏 return () => { isUnmounted = true; }; }, [memberArea, memberNum]); return /* 你的组件视图 */ null; }
额外渲染排查方向
如果实际渲染次数超过2次,按以下顺序排查:
- 检查父组件是否重渲染:当前组件如果没有用
React.memo包裹,父组件每次重渲染都会连带子组件重渲染,和子组件自身逻辑无关 - 检查是否在组件顶层无判断直接调用状态更新方法,这种情况会触发渲染死循环
- 检查
memberArea是否被重复赋值相同值,React18之前的版本不会自动跳过相同原始值的更新渲染
内容的提问来源于stack exchange,提问作者heyhye
相关产品推荐
相关产品推荐

