【问题标题】:React useEffect - passing a function in the dependency arrayReact useEffect - 在依赖数组中传递一个函数
【发布时间】:2020-10-17 10:57:32
【问题描述】:

当我将函数表达式传递到 useEffect 依赖数组时,为什么会创建一个无限循环?函数表达式不会改变组件状态,它只是引用它。

// component has one prop called => sections

const markup = (count) => {
    const stringCountCorrection = count + 1;
    return (
        // Some markup that references the sections prop
    );
};

// Creates infinite loop
useEffect(() => {
    if (sections.length) {
        const sectionsWithMarkup = sections.map((section, index)=> markup(index));
        setSectionBlocks(blocks => [...blocks, ...sectionsWithMarkup]);
    } else {
        setSectionBlocks(blocks => []);
    }
}, [sections, markup]);

如果标记改变了状态,我可以理解为什么它会创建一个无限循环,但它不是简单地引用 section 属性。

对于那些寻求解决这个问题的人

const markup = useCallback((count) => {
        const stringCountCorrection = count + 1;
        return (
            // some markup referencing the sections prop
        );
// useCallback dependency array
}, [sections]);

所以我不是在寻找这个问题的代码相关答案。如果可能的话,我正在寻找关于为什么会发生这种情况的详细解释。

我更感兴趣的是为什么,然后只是简单地找到解决问题的答案或正确方法。

为什么在 useEffect 之外声明的 useEffect 依赖数组中传递一个函数会导致在所述函数中的状态和道具都未更改时重新渲染。

【问题讨论】:

标签: reactjs react-hooks use-effect usecallback


【解决方案1】:

问题在于,在每个渲染周期中,markup 都会重新定义。 React 使用浅对象比较来确定值是否更新。每个渲染周期markup 都有不同的引用。您可以使用useCallback 来记忆函数,但参考是稳定的。您是否为您的 linter 启用了react hook rules?如果你这样做了,那么它可能会标记它,告诉你原因,并提出这个建议来解决参考问题。

const markup = useCallback(
  (count) => {
    const stringCountCorrection = count + 1;
    return (
      // Some markup that references the sections prop
    );
  },
  [count, /* and any other dependencies the react linter suggests */]
);

// No infinite looping, markup reference is stable/memoized
useEffect(() => {
    if (sections.length) {
        const sectionsWithMarkup = sections.map((section, index)=> markup(index));
        setSectionBlocks(blocks => [...blocks, ...sectionsWithMarkup]);
    } else {
        setSectionBlocks(blocks => []);
    }
}, [sections, markup]);

【讨论】:

  • 这完全有道理!太感谢了。我确实在考虑这样一个事实,即每次重新渲染组件时都会重新声明函数表达式。
  • 在没有只告诉传递函数的教程中,我可以找到为什么会发生无限循环。非常感谢简单的演示。如何从父级传递一个函数道具。该函数是否应该在父节点上进行记忆?
  • @kuzdogan 如果你阅读了useCallback 文档,他们解释说,钩子的主要用途之一是“返回一个回调的记忆版本,只有在其中一个依赖项发生变化时才会改变。这在将回调传递给依赖引用相等性以防止不必要的渲染的优化子组件时很有用。“您应该像往常一样编写和传递回调,并且只达到记忆必要时。 useMemo 钩子文档更详细地解释了这个原因。
  • @DrewReese 我发现这个解决方案很有帮助。基本上必须记住在上下文而不是组件中创建函数的位置。 stackoverflow.com/questions/64104731/…
  • @DrewReese 这个答案可能使我免于许多即将发生的错误。
【解决方案2】:

为什么我传递函数表达式时会创建一个无限循环

“无限循环”是组件反复重新渲染,因为 markup 函数是每次组件渲染时的新函数引用(内存中的指针),useEffect 触发重新渲染,因为它是依赖。

解决方案正如@drew-reese 指出的那样,使用useCallback 挂钩来定义您的markup 函数。

【讨论】:

    猜你喜欢
    • 2019-08-09
    • 2020-04-29
    • 2020-04-15
    • 2022-11-10
    • 1970-01-01
    • 2022-10-14
    • 2022-01-11
    • 2020-05-17
    • 2020-04-05
    相关资源
    最近更新 更多