React useEffect - 在依赖数组中传递一个函数

mma*_*n33 15 reactjs react-hooks usecallback use-effect

为什么在将函数表达式传递到 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]);
Run Code Online (Sandbox Code Playgroud)

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

对于那些正在寻找解决此问题的方法的人

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

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

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

为什么在 useEffect 依赖数组中传递一个在 useEffect 之外声明的函数会导致在所述函数中的 state 和 props 都没有改变时重新渲染。

Dre*_*ese 26

问题是在每个渲染周期中,markup都会重新定义。React 使用浅层对象比较来确定值是否更新。每个渲染周期markup都有不同的参考。您可以使用useCallback来记住该功能,因此参考是稳定的。你是否为你的 linter 启用了react 钩子规则?如果你这样做了,它可能会标记它,告诉你原因,并提出这个建议来解决参考问题。

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]);
Run Code Online (Sandbox Code Playgroud)

  • @kuzdogan如果您阅读了 [useCallback](https://reactjs.org/docs/hooks-reference.html#usecallback) 文档,他们会解释该钩子的主要用途之一是“返回回调的记忆版本仅当依赖项之一发生更改时才会更改。*这在将回调传递给依赖引用相等性来防止不必要的渲染的优化子组件时很有用。*“您*应该*照常编写和传递回调,并且仅达到记忆化必要时使用它们。`useMemo` 挂钩文档更详细地解释了其原因。 (2认同)
  • @DrewReese 我发现这个解决方案很有帮助。基本上必须记住在上下文中而不是组件中创建函数的位置。/sf/ask/4487331201/ (2认同)

Cor*_*son 9

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

“无限循环”是组件一遍又一遍地重新渲染,因为markup每次组件渲染时该函数都是一个新的函数引用(内存中的指针)并useEffect触发重新渲染,因为它是依赖项。

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