尽管依赖项没有变化,但 useEffect 运行无限循环

Tre*_*vor 13 reactjs react-hooks use-effect

我有使用钩子的函数组件。在useEffect钩子中,我只想从我的后端获取数据并将结果存储在 state 中。但是,尽管将数据变量添加为依赖项,但 useEffect 仍会在无限循环中触发 -即使数据没有更改。如何阻止 useEffect 连续触发?

我已经尝试了空数组 hack,它确实阻止了 useEffect 连续触发,但这不是所需的行为。例如,如果用户保存新数据,useEffect 应该再次触发以获取更新的数据 - 我不想模拟 componentDidMount。

const Invoices = () => {
  const [invoiceData, setInvoiceData] = useState([]);

  useEffect(() => {
    const updateInvoiceData = async () => {
      const results = await api.invoice.findData();
      setInvoiceData(results);
    };
    updateInvoiceData();
  }, [invoiceData]);

  return (
    <Table entries={invoiceData} />
  );
};
Run Code Online (Sandbox Code Playgroud)

我希望 useEffect 在初始渲染后触发,并且仅在 invoiceData 更改时再次触发。

jer*_*red 19

useEffect 依赖数组的工作方式是检查数组中所有项从前一个渲染和新渲染之间是否严格 (===) 等效。因此,将数组放入 useEffect 依赖数组是非常麻烦的,因为与 === 的数组比较是通过引用而不是内容来检查等效性

const foo = [1, 2, 3];
const bar = foo;
foo === bar; // true

const foo = [1, 2, 3];
const bar = [1, 2, 3];
foo === bar; // false
Run Code Online (Sandbox Code Playgroud)

在您的效果函数内部,当您执行此操作时,setInvoiceData(results)您正在更新invoiceData到一个新数组。即使那个新数组中的所有项都完全相同,对新数组的引用invoiceData也发生了变化,导致效果的依赖项不同,再次触发该函数——无穷无尽。

一种简单的解决方案是简单地invoiceData从依赖项数组中删除。这样,useEffect 函数的作用基本上和 componentDidMount 类似,它只会在组件第一次渲染时触发一次。

useEffect(() => {
    const updateInvoiceData = async () => {
      const results = await api.invoice.findData();
      setInvoiceData(results);
    };
    updateInvoiceData();
  }, []);
Run Code Online (Sandbox Code Playgroud)

这种模式非常常见(和有用),甚至在官方 React Hooks API 文档中也提到过:

如果您只想运行效果并仅将其清理一次(在装载和卸载时),您可以传递一个空数组 ([]) 作为第二个参数。这告诉 React 你的 effect 不依赖于来自 props 或 state 的任何值,所以它永远不需要重新运行。这不是作为特殊情况处理的——它直接遵循依赖项数组的工作方式。