使用中的无限循环

我一直在使用React 16.7-alpha中的新钩子系统,并在我处理的状态是对象或数组时陷入useEffect的无限循环中。

首先,我使用useState并使用一个空对象启动它,如下所示:

const [obj, setObj] = useState({});

然后,在useEffect中,我再次使用setObj将其设置为空对象。作为第二个参数,我传递了[obj],希望如果对象内容没有更改,它也不会更新但是它一直在更新。我猜是因为不管内容如何,​​这些总是不同的对象,这使React认为它不断变化?

useEffect(() => {
  setIngredients({});
}, [ingredients]);

数组也是如此,但作为基元,它不会像预期的那样卡在循环中。

使用这些新的挂钩,在检查天气内容是否更改时,应该如何处理对象和数组?

Near神奇2020/03/18 18:35:19

如果在useEffect的末尾包含空数组

useEffect(()=>{
        setText(text);
},[])

它会运行一次。

如果在数组上还包含参数:

useEffect(()=>{
            setText(text);
},[text])

每当文本参数更改时,它将运行。

LEY神无2020/03/18 18:35:19

如果使用此优化,请确保数组包含组件作用域中所有随时间变化并被效果使用的值(例如props和state)。

我相信他们正在尝试表达使用过时数据的可能性,并意识到这一点。array只要我们知道如果这些值中的任何一个发生更改,它就会执行效果,我们在第二个参数中发送的值的类型无关紧要如果要ingredients在效果内将其用作计算的一部分,则应将其包括在中array

const [ingredients, setIngredients] = useState({});

// This will be an infinite loop, because by shallow comparison ingredients !== {} 
useEffect(() => {
  setIngredients({});
}, [ingredients]);

// If we need to update ingredients then we need to manually confirm 
// that it is actually different by deep comparison.

useEffect(() => {
  if (is(<similar_object>, ingredients) {
    return;
  }
  setIngredients(<similar_object>);
}, [ingredients]);

乐猪猪2020/03/18 18:35:19

您的无限循环是由于圆度

useEffect(() => {
  setIngredients({});
}, [ingredients]);

setIngredients({});会更改的值ingredients(每次都会返回一个新引用),该值将运行setIngredients({})要解决此问题,可以使用以下两种方法之一:

  1. 将另一个第二个参数传递给useEffect
const timeToChangeIngrediants = .....
useEffect(() => {
  setIngredients({});
}, [timeToChangeIngrediants ]);

setIngrediants将在timeToChangeIngrediants更改后运行

  1. 我不确定一旦更改了哪个用例就能证明更改要素的合理性。但是,在这种情况下,您Object.values(ingrediants)可以将第二个参数传递给useEffect。
useEffect(() => {
  setIngredients({});
}, Object.values(ingrediants));
小胖理查德2020/03/18 18:35:19

如果要构建自定义钩子,有时会导致无限循环,默认情况如下:

function useMyBadHook(values = {}) {
    useEffect(()=> { 
           /* This runs every render, if values is undefined */
        },
        [values] 
    )
}

解决方法是使用同一对象,而不是在每个函数调用上创建一个新对象:

const defaultValues = {};
function useMyBadHook(values = defaultValues) {
    useEffect(()=> { 
           /* This runs on first call and when values change */
        },
        [values] 
    )
}
神乐凯2020/03/18 18:35:19

我不确定这是否适合您,但是您可以尝试添加.length,如下所示:

useEffect(() => {
        // fetch from server and set as obj
}, [obj.length]);

就我而言(我正在获取一个数组!),它在安装时获取数据,然后仅在更改时获取数据,并且没有进入循环。

Stafan村村达蒙2020/03/18 18:35:18

如文档(https://reactjs.org/docs/hooks-effect.html)中所述,当您希望在每次渲染后执行一些代码时useEffect,可以使用钩子从文档:

useEffect是否在每次渲染后运行?是!

如果要对此进行自定义,则可以按照同一页面稍后出现的说明进行操作(https://reactjs.org/docs/hooks-effect.html#tip-optimizing-performance-by-skipping-effects)。基本上,该useEffect方法接受第二个参数,React将检查该参数以确定是否必须再次触发效果。

useEffect(() => {
  document.title = `You clicked ${count} times`;
}, [count]); // Only re-run the effect if count changes

您可以将任何对象作为第二个参数传递。如果此对象保持不变,则只会在第一次安装后触发效果如果对象发生变化,效果将再次被触发。

泡芙西里2020/03/18 18:35:18

将空数组作为第二个参数传递给useEffect会使它仅在安装和卸载时运行,从而停止任何无限循环。

useEffect(() => {
  setIngredients({});
}, []);

我在https://www.robinwieruch.de/react-hooks/上关于React hooks的博客文章中向我阐明了这一点。