简体   繁体   English

使用默认参数和 useEffect 防止函数组件无限循环

[英]Prevent infinite loop on function component with default parameter and useEffect

When creating a function component in React and setting a default parameter everything works like expected and the component will be rendered once.在 React 中创建函数组件并设置默认参数时,一切都按预期工作,并且组件将被渲染一次。 But as soon as you add a hook like useEffect and use this parameter in the dependency array the component rerenders forever.但是一旦你添加了一个像useEffect这样的useEffect并在依赖数组中使用这个参数,组件就会永远重新渲染。 I've created a simple demo here: https://codesandbox.io/s/infinite-useeffect-loop-on-default-value-tv7hj?file=/src/TestComponent.jsx我在这里创建了一个简单的演示: https : //codesandbox.io/s/infinite-useeffect-loop-on-default-value-tv7hj?file=/ src/ TestComponent.jsx

The reason is quite obvious, because when using an object as default parameter, it will be created again and will not be equal to the previous one.原因很明显,因为当使用一个对象作为默认参数时,它会被再次创建并且不会等于前一个。 And of course this doesn't happen on primitive default parameter values like number or string.当然,这不会发生在原始默认参数值(如数字或字符串)上。

Is there any better way to avoid this side effect besides using defaultProps ?除了使用defaultProps之外,还有什么更好的方法可以避免这种副作用?

Yes, instead of setting the default value of value to being an object, just set it to false.是的,而不是默认值设定value来作为一个对象,只需将其设置为false。 Then check if value is truthy, if it is, then access the correct properties, otherwise, just show a default value.然后检查value是否为真,如果是,则访问正确的属性,否则,仅显示默认值。 New code . 新代码

It would be something like:它会是这样的:

import { useEffect, useState } from "react";

const TestComponent = ({ value = false }) => {
  const [calcValue, setCalcValue] = useState(0);

  useEffect(() => {
    setCalcValue((cur) => cur + 1);
  }, [value]);

  return (
    <div>
      {value ? value.name : "Test"}:{calcValue}
    </div>
  );
};

The reason you get infinite loops is because the reference of value keeps changing.你得到无限循环的原因是因为value的引用不断变化。

The first time the component is rendered, it sees a new reference to value, which triggers the useEffect , which in turns modifies the state of the component, and this leads to a new render, which causes value to be re-created once again because the old reference to that variable has changed.第一次渲染组件时,它看到一个对 value 的新引用,这会触发useEffect ,它反过来修改组件的状态,这导致新的渲染,这会导致再次重新创建value ,因为对该变量的旧引用已更改。

The easiest way to deal with this is to just create a default value outside the component and use that ( basically the same the as the defaultProps solution ):解决这个问题的最简单方法是在组件外部创建一个默认值并使用它(defaultProps解决方案基本相同):

import { useEffect, useState } from "react";

const defaultValue = {name: "Test"}; // <-- default here
const TestComponent = ({ value = defaultValue }) => {
  const [calcValue, setCalcValue] = useState(0);

  useEffect(() => {
    setCalcValue((cur) => cur + 1);
  }, [value]);

  return (
    <div>
      {value.name}:{calcValue}
    </div>
  );
};

Doing this will ensure that each time the component renders, it sees the same reference for for value , therefore the useEffect hook only runs once.这样做将确保每次渲染组件时,它都会看到对value的相同引用,因此useEffect钩子只运行一次。


Another way of dealing with this is to first wrap your component with memo , then create a new state variable which takes on the original value , and make your useEffect hook depend on this new state variable:解决这个问题的另一种方法是首先用memo包装你的组件,然后创建一个采用原始value的新状态变量,并使你的useEffect钩子依赖于这个新状态变量:

const TestComponent = React.memo(({ value = {name: "Test"} }) => {
  const [calcValue, setCalcValue] = useState(0);
  const [myValue, setMyValue] = useState(value);

  useEffect(() => {
    setCalcValue((cur) => cur + 1);
  }, [myValue]);

  return (
    <div>
      {myValue.name}:{calcValue}
    </div>
  );
});

The reason why we wrap the component with memo is so that it only re-renders after a state change if the prop had changed in value (instead of reference ).我们用memo包装组件的原因是,如果prop 的(而不是reference )发生了变化,它只会在状态更改后重新渲染。 You can change the way memo detects props changes by providing a custom comparison function as a second parameter.您可以通过提供自定义比较函数作为第二个参数来更改memo检测道具更改的方式。

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

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