【问题标题】:Why does adding a memoized function in useEffect dependency array cause an error?为什么在 useEffect 依赖数组中添加 memoized 函数会导致错误?
【发布时间】:2022-01-07 20:09:03
【问题描述】:

我的useEffect 挂钩中使用了一个函数,我必须添加dependency array。我还有一些其他依赖项作为来自外部组件的道具传递。由于该依赖函数,我不得不将我的函数包装在 useCallback 中,以避免在每次渲染时都发生更改。但我很困惑为什么错误仍然弹出?还有我应该怎么过呢?

import _ from "lodash";
import React, { useCallback, useEffect, useState } from "react";

function App({uniqueCol,data,handleSelect, unselectAll = false}) {
  const [selected, setSelected] = useState(new Set(""));
  const [currentRows, setCurrentRows] = useState(data);

  //here it is fine when I add currentRows and uniqueCol as dependencies
  const changeSelect = useCallback(
    (id: string, status: boolean) => {
      const cr = currentRows.map((row) => {
        if (uniqueCol) {
          // @ts-ignore
          if (row[uniqueCol] == id) row.selected = status;
        }
        return row;
      });
      setCurrentRows(cr);
    },
    [currentRows, uniqueCol]
  );

  useEffect(() => {
    selected.forEach((sel) => changeSelect(sel, true));
    setCurrentRows(data);
  }, [changeSelect, data, selected]);


  /*here it causes errors when I add changeSelect, currentRows, 
          handleSelect as dependencies but works fine when I remove them*/

  const unSelectAll = useCallback(() => {
    setSelected(new Set(""));
    _.map(currentRows, "id").forEach((val) => {
      changeSelect(val, false);
    });

    if (handleSelect) handleSelect([]);
  }, [changeSelect, currentRows, handleSelect]);


//another useEffect that is using the function unSelectAll as a dependency     
useEffect(() => {
         unSelectAll();
      }, [unSelectAll, unselectAll]);

    

  return <h1>Anything here</h1>;
}

我得到的错误是:

react-dom.development.js:67 警告:超过最大更新深度。当组件在 useEffect 中调用 setState 时,可能会发生这种情况,但 useEffect 要么没有依赖数组,要么每次渲染时其中一个依赖项都发生了变化。

【问题讨论】:

  • 能贴一下useEffect代码吗?
  • handleSelect 来自组件的外部。它不能确定它是否正在改变。也许记忆可以帮助const memorizedHandler = React.useMemo(()=&gt;handleSelect, [handleSelect]);,然后使用memorizedHandler。如果它不起作用,请使用 useRef。
  • 我用了这个然后这段代码也造成了无限渲染:useEffect(() =&gt; { selected.forEach((sel) =&gt; changeSelect(sel, true)); setCurrentRows(data); }, [changeSelect, data, selected]);

标签: javascript reactjs frontend


【解决方案1】:

您发布的代码不包含“完整故事”,但我认为假设以下内容是合理的,因为您说 “我必须将我的函数包装在 useCallback 中以避免在每次渲染时进行更改", 但是您对useCallback() 的使用在这里几乎没有效果:

changeSelect 在每次渲染时都会改变,因为:

  • changeSelect 在依赖项中有 currentRows,并且
  • changeSelect 致电setCurrentRows()

unSelectAll 在每次渲染时都会改变,因为:

  • unSelectAll 在依赖项中有 changeSelect,并且
  • changeSelect 每次渲染都会发生变化

因此,说明“递归”依赖关系的简化版本是:

function App(){
  const [currentRows, setCurrentRows] = useState(data);

  const changeSelect = useCallback(
    () => { setCurrentRows([]); },
    [ currentRows, uniqueCol ]
  );

  const unSelectAll = useCallback(
    () => { changeSelect(); },
    [ changeSelect, currentRows ]
  );

  return <>...</>;
}

这只是一个猜测,但可能还有一些其他代码 - 可能是您提到的 useEffect,但尚未发布代码 - 正在使用 unSelectAll 作为依赖,可能会更改uniqueCol,所以

  • 如果uniqueCol 发生变化,App 会重新渲染
  • changeSelect 每次渲染都会发生变化
  • unSelectAll 更改,因为 changeSelect 更改
  • uniqueCol 更改是因为 unSelectAll 更改(可能?在其他代码中?)

至少这可能是根本原因,因为这个或类似的原因,实际的递归链(无限循环)在其他地方关闭。

【讨论】:

  • 我添加了 useEffect ,它使用函数 unSelectAll 作为依赖项。你能建议我如何更改我的代码以避免错误吗?
  • 我还添加了一些可能至关重要的 useEffect。
  • 我怎样才能让changeSelect在每次渲染时都不会改变。
  • 如果 useEffect 中的状态在同一个 useEffect 的依赖项中,则不要更改该状态。例如。不要在changeSelect 中使用currentRows,而是使用a function in setState。仅供参考:row.selected = status 以后可能还会导致错误 (never mutate state directly)。但如果需要,请提出一个新问题。
  • row.selected = status 发生在地图内,因此不直接在状态上。另外我认为直接改变站点不会导致无限循环,而是会导致其他一些行为。问题是询问问题的原因以及如何解决问题。
猜你喜欢
  • 2012-11-13
  • 2020-02-26
  • 2021-07-25
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-11-27
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多