为何下述ReactJS代码会触发无限重渲染循环?
问题描述
原本期望在__payload变化时执行useEffect的代码:
const Viewer = (props) => { const __payload = JSON.parse(props.payload); const [values, setValues] = useState([]); useEffect( () => { var __values = "value"; // 传入字符串 setValues(__values); }, [__payload]); };
但当向setValues传入数组值时,出现无限循环并抛出错误:
const Viewer = (props) => { const __payload = JSON.parse(props.payload); const [values, setValues] = useState([]); useEffect( () => { var __values = ["value"]; // 传入数组 setValues(__values); }, [__payload]); };
错误提示:
Warning: Maximum update depth exceeded. This can happen when a component calls setState inside useEffect, but useEffect either doesn't have a dependency array, or one of the dependencies changes on every render.
明明已经限定useEffect仅在__payload变化时执行,为何会出现这种情况?
原因分析
核心问题出在__payload的创建方式和React依赖比较的逻辑上:
引用类型与基本类型的比较差异:
React检查useEffect依赖时使用Object.is规则:字符串这类基本类型只要值相等,就会被判定为相同;但数组、对象这类引用类型,哪怕内容完全一致,只要是不同的内存引用,就会被判定为不同。每次渲染都会生成新的
__payload引用:
不管props.payload有没有变化,每次组件渲染时,JSON.parse(props.payload)都会返回全新实例:- 如果解析结果是字符串,值相同的情况下
Object.is判定为相等,useEffect不会重复执行; - 如果解析结果是数组/对象,每次都是新的引用,React会认为
__payload每次都变了,于是反复触发useEffect。
- 如果解析结果是字符串,值相同的情况下
无限循环触发链:
useEffect执行 → 调用setValues更新状态 → 组件重新渲染 → 生成新的__payload引用 →useEffect再次触发 → 循环往复,最终超出渲染深度限制。
解决办法
方法1:用useMemo缓存解析后的__payload
把解析操作包裹在useMemo里,只在props.payload变化时重新解析,保证__payload的引用稳定:
const Viewer = (props) => { const __payload = useMemo(() => JSON.parse(props.payload), [props.payload]); const [values, setValues] = useState([]); useEffect( () => { var __values = ["value"]; setValues(__values); }, [__payload]); };
方法2:直接依赖props.payload
既然__payload是从props.payload解析而来,可跳过中间变量,直接把props.payload作为useEffect的依赖,只有当原字符串变化时才触发:
const Viewer = (props) => { const [values, setValues] = useState([]); useEffect( () => { const __payload = JSON.parse(props.payload); var __values = ["value"]; setValues(__values); }, [props.payload]); };
内容的提问来源于stack exchange,提问作者VilleLipponen
相关产品推荐
相关产品推荐

