React useState触发Maximum update depth exceeded报错解决方案
报错根因
这个无限循环报错来自两个规则的共同作用:
useEffect/useLayoutEffect未传入第二个依赖数组参数时,会在组件每一次渲染完成后都执行,而非仅挂载时执行一次。- React 调用 state setter 时,会通过
Object.is()对比新旧状态:如果值不相等就触发重渲染,如果值相等就直接跳过更新,不会触发重渲染流程。
你的代码触发无限循环的完整流程:
- 初始
roles状态是空数组[],组件首次渲染完成后执行effect,调用setRoles(["5nxg5wvb"])。 - 这里传入的
["5nxg5wvb"]是每次执行effect时新创建的数组引用,属于引用类型,哪怕内容和之前的状态一致,Object.is()对比两个不同内存地址的数组永远返回false,React判定状态变化,触发组件重渲染。 - 重渲染完成后effect再次执行,又生成一个新的数组传入setRoles,再次触发重渲染,循环往复直到触及React的更新嵌套上限,抛出对应错误。
为什么传入字符串就不报错:
字符串是JavaScript原始值类型,当你调用setRoles("5nxg5wvb")时,第一次更新后roles的值已经是这个字符串,后续effect重复执行时传入的同内容字符串和现有状态做Object.is()对比会返回true,React判定状态没有变化,直接跳过重渲染,循环自然终止,报错消失。把useLayoutEffect换成useEffect只是执行时机不同,没有改变上面的循环逻辑,所以报错完全一致。
修复方式
根据实际场景选对应方案即可:
- 如果只需要在组件挂载时给roles赋一次初始值,直接给effect加空依赖数组,让effect仅在首次挂载时执行一次:
const [roles, setRoles] = useState([]); useLayoutEffect(() => { setRoles(["5nxg5wvb"]); }, []); // 新增空依赖数组
- 如果这个值不需要后续动态修改,直接把初始值写在
useState的参数里,省掉effect里的额外更新,性能更好:
const [roles, setRoles] = useState(["5nxg5wvb"]);
- 如果需要根据其他状态动态更新roles,就把对应依赖写到依赖数组里,同时做新旧值判断,避免传入同内容的新引用触发无意义更新。
内容的提问来源于stack exchange,提问作者Alicia
相关产品推荐
相关产品推荐

