繁体   English   中英

React:useEffect “悖论”与 eventHandler 道具

[英]React: useEffect “paradox” with eventHandler props

这个例子有问题。

我有一个Input组件。 这个输入组件有一个onChange属性来处理内部的任何数据更改。

应该是,如果我从外部更新value ,则不会触发onChange道具,因为输入组件内部没有任何更改。 如果你开始输入onChange道具就会被触发,我也可以从外部更新状态。

但是如果满足某些条件(在我的示例中只是一个布尔状态),我想从内部预填充输入字段,因此我必须调用onChange事件,以便应用程序可以从外部更新状态。

问题/悖论是useEffect检查条件现在需要onChange道具作为依赖项,并且由于此道具是一种方法,因此它会在每次渲染时更改。 这意味着现在输入元素总是在任何更改时重置为 useEffect 的值,因为 useEffect 再次触发。

如果允许我从 useEffect 依赖项中删除onChange属性,我的问题就不存在了,但这当然违反了 useEffect 钩子的规则。

我想到的唯一“解决方案”:

  • 我可以将 Input 元素之外的条件移动到 App 组件中。 这对我的示例来说是有道理的,但在实际场景中,每次使用组件时都应该相同,而且我不想每次从外部重复该条件,因为条件也不仅仅是真实的布尔值场景当然。
  • 我可以添加一个额外的状态,条件是否已经被检查,并且只有在它尚未被触发时才触发 useEffect。 但这会很快变得非常复杂,如果条件需要在某个时候再次检查,等等。

我在不同的形状场景中遇到过这个问题,所以我真的希望得到一个可以应用于一般类似场景的答案。 也许我的代码中有一个关于如何使用事件处理程序的反模式?

预先感谢您的所有答案^^。

我会将 if 语句更改为仅在prefillCondition已更改时运行。 这意味着您需要知道上次渲染的值是多少。 这样做内联看起来像:

const [prefillCondition, setPrefillCondition] = React.useState(false);
const previous = useRef();
React.useEffect(() => {
  previous.current = prefillCondition;
})
React.useEffect(() => {
  // On the first render, previous.current will be undefined. On every render after that
  // it will tell you what value prefillCondition had on the previous render.
  if (prefillCondition && !previous.current) {
    onChange("value from useEeffff");
  }
}, [onChange, prefillCondition, previous]);

我发现想要记住上一个渲染中的值是很常见的。 如果您也这样做,您可能需要考虑将该逻辑提取到自定义钩子中:

const usePrevious = (value) => {
  const previous = useRef();
  React.useEffect(() => {
    previous.current = value;
  })
  return previous.current;
}

// used like:
const [prefillCondition, setPrefillCondition] = React.useState(false);
const prevPrefillCondition = usePrevious(prefillCondition);
React.useEffect(() => {
  if (prefillCondition && !prevPrefillCondition) {
    onChange("value from useEeffff");
  }
}, [onChange, prefillCondition, prevPrefillCondition]);

考虑这个

const a = () => 'foo bar'
const b = () => 'foo bar'

a == b // false

即使该函数具有相同的代码,它也具有不同的内存引用,因此对于 React 而言,它始终显示为一个新的(已更改的)道具,这就是它调用重新渲染的原因,并且由于您总是传递新的匿名 onChange 回调,因此它每次都会调用 useEffect。

所以你可以从依赖项数组中省略它(注意 ESlint 规则)

React.useEffect(() => {
    // this would supposibly fetch some default value based on conditions and the call an onChange
    if (prefillCondition) {
      onChange("value from useEeffff");
    }
  }, [prefillCondition]); // eslint-disable-line react-hooks/exhaustive-deps

或使用React.useCallback()函数,它可以准确地解决您的问题并防止在每次渲染时创建新函数。


const App = () => {
  const [myVal, setMyVal] = React.useState("");

  const handler = React.useCallback((a) => {
    console.log("onchange", a);
    setMyVal(a);
  }, []);

  return (
    <div>
      <Input value={myVal} onChange={handler} />
      <div>
        <button
          onClick={() => {
            setMyVal("smth");
          }}
        >
          set to smth from outside
        </button>
      </div>
    </div>
  );
};

你不能只在输入上设置一个默认值

defaultValue={prefillCondition ? "text here" : ""}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM