React组件两类报错:短时间内Location/History调用过多及更新深度超限
解决React组件的最大更新深度和Location API调用过多问题
问题根源分析
1. 最大更新深度警告
第二个useEffect依赖包含isOpen,调用onOpen()会更新isOpen状态,触发组件重新渲染,导致该useEffect再次执行。初始状态下agency设为[],会直接进入onOpen()分支触发状态更新,加上fetch完成后agency变化再次触发useEffect,形成渲染循环隐患。
2. Location API调用过多报错
如果fetch返回的data.agency为null,useEffect会执行redirect,触发路由导航导致组件卸载重挂载,重新fetch后仍得到agency为null,再次触发redirect,形成无限导航循环,短时间内多次调用History API引发报错。
修复方案
步骤1:修正状态初始值与判断逻辑
将agency初始值从[]改为null,确保仅在fetch完成后执行判断逻辑,避免初始阶段误触发分支。
步骤2:防止无限导航循环
用useRef标记是否已执行过redirect,避免重复触发导航。
步骤3:优化依赖与逻辑
移除不必要的useMemo,直接使用agency作为useEffect依赖;添加useRef标记是否已打开modal,防止重复调用onOpen()。
修改后的代码
const OwnerPage = () => { const onOpen = useAgencyModal((state) => state.onOpen); const isOpen = useAgencyModal((state) => state.isOpen); const params = useParams(); const [owner, setOwner] = useState<User | null>(null); // 修正初始值为null,匹配后续判断逻辑 const [agency, setAgency] = useState<any | null>(null); // 标记是否已执行过redirect const hasRedirected = useRef(false); // 标记是否已打开过modal const hasOpenedModal = useRef(false); useEffect(() => { const fetchData = async () => { try { const response = await fetch(`/api/owner/${params.ownerId}`, { method: "GET", }); const data = await response.json(); setAgency(data.agency); setOwner(data.owner); } catch (error) { console.error("Error occurred while fetching data:", error); } }; fetchData(); }, [params.ownerId]); useEffect(() => { // 数据未加载完成时不执行逻辑 if (agency === null && owner === null) return; if (agency !== null) { // 仅在modal未打开且未触发过打开操作时调用onOpen if (!isOpen && !hasOpenedModal.current) { onOpen(); hasOpenedModal.current = true; } } else { // 仅在未执行过redirect时触发导航 if (!hasRedirected.current) { hasRedirected.current = true; redirect(`/dashboard/owner/${params.ownerId}/dashboard`); } } }, [agency, owner, isOpen, onOpen, params.ownerId]); return null; };
额外说明
- 移除冗余的
useMemo,直接使用agency和owner作为依赖,简化代码且不影响性能。 useRef标记状态不会触发组件重渲染,有效避免依赖变化导致的重复执行问题。- 增加数据加载完成判断,确保逻辑仅在fetch返回结果后执行,消除初始阶段的误触发。
内容的提问来源于stack exchange,提问作者Trd Med
相关产品推荐
相关产品推荐

