修复useEffect无限循环:useState数组作为依赖项问题
问题原因
无限循环的触发链路非常清晰:
- 你的
useEffect依赖列表同时声明了myArray本地状态、anArrayFromProps入参 - effect 内部每次执行都会调用
setMyArray传入新生成的数组,React对数组、对象这类引用类型的状态对比是判断引用地址是否相等,哪怕新旧数组内容完全一致,新生成的数组引用也和旧状态不一样 - 依赖对比时发现
myArray引用变了,就会再次触发effect执行,effect执行又更新myArray生成新引用,反复触发就形成了死循环,和你本身写的数组遍历计算逻辑没有关系。
修复方案
根据你的业务需求「仅当传入prop更新时触发effect执行」,分两种场景处理:
场景1:effect内对旧数组的计算不需要响应myArray自身更新
你示例里写的数组遍历只是做临时计算、不需要每次myArray变化就重跑effect的话,直接把myArray从依赖数组中移除即可,完全匹配业务触发逻辑:
const HooksAreWeird = ({anArrayFromProps}) => { const [myArray, setMyArray] = React.useState(['a', 'b', 'c']); React.useEffect(() => { // 对旧myArray做计算 myArray.forEach(el => { const someCalculation = el.indexOf('a'); }); // 基于prop生成新数组更新状态 const newArray = anArrayFromProps.map(el => el.letterFromAlphabet); setMyArray(newArray); console.log("------useEffect") }, [anArrayFromProps]); // 仅保留prop依赖,只在prop更新时触发 return ( <div> {myArray.map(el => ( <span key={el}> {el} </span> ))} </div> ); };
场景2:effect内确实需要读取最新的myArray值参与计算
如果你的计算逻辑强依赖更新前最新的myArray值,又不想把myArray加入依赖触发循环,直接用React提供的函数式状态更新写法即可:在setMyArray里传入回调,回调的入参就是上一次的最新状态值,不需要把myArray加入依赖也能拿到正确的值,从根源避免循环:
const HooksAreWeird = ({anArrayFromProps}) => { const [myArray, setMyArray] = React.useState(['a', 'b', 'c']); React.useEffect(() => { setMyArray(prevMyArray => { // prevMyArray永远是当前最新的myArray状态值 prevMyArray.forEach(el => { const someCalculation = el.indexOf('a'); }); // 回调返回值会作为新的myArray状态 return anArrayFromProps.map(el => el.letterFromAlphabet); }) console.log("------useEffect") }, [anArrayFromProps]); return ( <div> {myArray.map(el => ( <span key={el}> {el} </span> ))} </div> ); };
注意:不要为了屏蔽hooks依赖校验的eslint警告随意加依赖,也不要直接用注释禁用eslint规则,优先用函数式更新、状态抽离的方式适配规则,否则很容易出现闭包捕获旧值的隐性bug。
内容的提问来源于stack exchange,提问作者imareactdeveloper
相关产品推荐
相关产品推荐

