React嵌套组件useEffect出现无限循环或不触发问题如何解决?
问题根因
- 无限循环原因:你给useEffect省略了依赖数组,这会导致每次组件渲染完成后都会执行useEffect回调,回调里调用
setLots会触发组件重渲染,重渲染后又会再次执行useEffect,形成死循环。 - 加依赖后空白原因:
- 如果你把依赖设为
[],useEffect只会在组件首次挂载时执行1次,此时API大概率还没返回数据,props.lots是空值/空数组,后续API返回数据后props.lots更新,useEffect也不会重新执行,本地state一直是初始的空值,所以渲染为空。 - 如果你把依赖设为
lots.form/lots.listLots这类本地state变量,会出现两种情况:要么依赖值不变,useEffect不执行,state没法同步最新的props;要么依赖值被setter更新后,再次触发useEffect执行setter,又回到死循环的问题。
- 如果你把依赖设为
- 额外冗余问题:父子组件都在把传入的props原样拷贝到本地state,完全没有必要,属于多余的状态维护,平白增加了同步成本。
最优解决方案
第一步:删除父组件冗余的state和useEffect
你完全不需要在父组件单独维护lots状态,直接用传入的props渲染即可,代码可以简化为:
const ListOfLots = (props) => { return ( <div> { // 加可选链避免初始空值报错 props.lots?.map(function(lot) { return <Lot key={lot.uuid} lot={lot} procForm={props.form}/> }) } </div> ) }
这样完全不需要useEffect,自然不会有死循环,props更新时组件会自动重渲染同步最新数据。
第二步:删除子组件冗余的state
子组件同样不需要把props拷贝到本地state,直接用传入的props即可:
const Lot = (props) => { let getWinningBid = (offers) => { for (let i = 0; i < offers.length; i++) { console.log("BOOM!!!"); if (offers[i].isTrue === true) { return offers[i].pip; } } } return ( // 渲染逻辑里直接用 props.lot、props.procForm 即可 ... ) }
特殊场景兼容(确实需要把props存为本地state的情况)
如果你的业务逻辑需要在本地修改状态不直接同步到父组件,必须维护本地state的话,正确的useEffect写法如下:
const ListOfLots = (props) => { const [lots, setLots] = useState({ listLots: props.lots, form: props.form }); // 依赖设置为传入的props属性,而非本地state useEffect(() => { // 可选:加浅比较/深比较,避免不必要的更新 if (JSON.stringify(lots.listLots) !== JSON.stringify(props.lots) || lots.form !== props.form) { setLots({ listLots: props.lots, form: props.form }); } }, [props.lots, props.form]); return ( <div> { lots.listLots?.map(function(lot) { return <Lot key={lot.uuid} lot={lot} procForm={lots.form}/> }) } </div> ) }
内容的提问来源于stack exchange,提问作者Misha
相关产品推荐
相关产品推荐

