Javascript React Hooks-在不触发无限循环的情况下更新父组件

Javascript React Hooks-在不触发无限循环的情况下更新父组件,javascript,reactjs,react-hooks,use-effect,Javascript,Reactjs,React Hooks,Use Effect,假设我有一个父组件,它呈现一个子组件,而子组件所做的只是保持一些状态,并在自己的状态发生变化时触发onChange处理程序。请参阅下面的代码或CodeSandbox 这段代码进入了一个无限循环,我想摆脱这种行为 可能的解决方案,但我不喜欢: 1:我可以将所有状态放在父组件中,并用父组件控制子组件,但这并不是我真正想要的。在现实生活中,我的子组件处理的不仅仅是一个简单的计数器,我想从父组件轻松地使用它。子组件内部有复杂的行为,我想向父组件传达一些简单的更改。或者这是一个绝对不允许的反应?(为了使

假设我有一个父组件,它呈现一个子组件,而子组件所做的只是保持一些状态,并在自己的状态发生变化时触发onChange处理程序。请参阅下面的代码或CodeSandbox

这段代码进入了一个无限循环,我想摆脱这种行为

可能的解决方案,但我不喜欢:

1:我可以将所有状态放在父组件中,并用父组件控制子组件,但这并不是我真正想要的。在现实生活中,我的子组件处理的不仅仅是一个简单的计数器,我想从父组件轻松地使用它。子组件内部有复杂的行为,我想向父组件传达一些简单的更改。或者这是一个绝对不允许的反应?(为了使状态保持在child中,并在状态更新到parent时触发更改)我想说不一定

2:我还可以从handleInternalChange处理程序中触发onChange处理程序。而且,不是我想要的。在现实生活中,我的子组件将从组件本身的几个独立位置进行更新,因此状态更改是观察和触发onChange父级的最优雅的事情

3:我可以在useEffect依赖项数组中省略onChange依赖项。这是不推荐的,React社区指。我明白,只有我觉得这种情况会是个例外?**此外,我使用CRA,它附带了大量现成的linter等,如果我从依赖项中删除onChange处理程序,linter会抱怨这一点,我不喜欢开始编写自己的线性规则。对于像我这样的简单用例,社区设置的过梁应该可以很好地工作

我认为会发生什么 我认为发生的是,父对象得到更新,然后重新呈现整个对象,并且不知何故,onChange处理程序也被“更改”。正如我所说,函数实际上并没有改变,但React认为(或知道)它会改变,因此它在子组件中再次触发useffect调用,然后无限循环就诞生了

但是,就我而言,onChange函数并没有改变。那么为什么会触发useEffect调用呢?我怎样才能防止这种情况

import React,{useState,useffect}来自“React”;
常量Comp=({onChange})=>{
常量[internalState,setInternalState]=useState(0);
常量handleChange=()=>{
setInternalState(internalState+1);
};
useffect(()=>{
const result=internalState.toString();
一次改变(结果);
},[internalState,onChange]);
返回(
点击我
{`internalnum:${internalState}`}
);
};
导出默认函数App(){
常量[状态,设置状态]=使用状态(“”);
常量handleChange=()=>setState(状态+1);
返回(
{`STATE:${STATE}`}
);
}

**不过,在其他情况下,onChange道具当然可以更改,只需为道具指定不同的函数即可。所以这条规则对我来说非常清楚。只是(如上一段所述)为什么在这种情况下它的行为会发生变化?因为我的函数一点也不改变。

你应该用useCallback钩子包装handleChange方法,这样它就会被创建一次

const handleChange = useCallback(() => setState(state + 1),[]);
发生无限循环是因为您在
组件中添加了
onChange
方法作为useffect的依赖项

useEffect接受依赖项数组,并在其中一个依赖项发生更改时运行该效果

由于您已将onchange处理程序添加为依赖项,因此每次父组件重新呈现时,都会创建一个新的
handleChange
方法实例,该实例与以前的handleChange方法不同

组件渲染流程如下所示:

  • 组件创建
    handleChange
    方法并将其传递给
  • 初始渲染后将调用
    中的Useffect,并从那里调用
    组件的handleChange方法
  • 组件中声明更改并重新呈现它。重新呈现时,将创建handleChange的新实例,并将onChange prop传递给
    组件
  • 由于previous和new onChange prop的值不同,将触发useEffect,这将再次更新父组件的状态,并且此循环将继续
  • 为了防止这种情况,handleChange方法应该用useCallback包装。传递给useCallback hook的回调函数将被记忆,因此当子组件比较新旧属性时,它们保持相等

    {} === {}  // false
    [] === []  // false
    (() => {}) == (() => {})  //false
    

    你的
    useffect
    在这里真的没有意义,。你得到了一个infinate循环,因为你的
    useffect
    每次都会被调用,而这个效果会调用
    onChange
    回调,所以无限循环。
    因为我的函数根本没有改变。
    每次渲染父组件时它都会改变。你的
    onChange
    道具会不断改变,因为每次父组件改变时它都会重新实例化
    handleChange
    ,这将永远不会是它以前的自我。我认为您应该使用解决方案1:将状态保留在父组件中,并将其作为道具传递给子组件;这不应该以任何方式限制您,因为您的子组件仍然可以有自己的本地状态,并执行它必须执行的所有复杂操作