【问题标题】:React useEffect forces me to add dependencies that trigger an infinite loopReact useEffect 强制我添加触发无限循环的依赖项
【发布时间】:2020-11-10 07:46:10
【问题描述】:

为什么 React 强迫我使用他们的 linter 插件添加我不想要的依赖项?

例如,我希望我的效果仅在某个特定值更改时触发,但 linter 告诉我将偶数函数添加到依赖项中,而我不希望这样。

为什么它强迫我这样做?我能从中得到什么?

  /**
   * Gets all items, pages, until 250th.
   */
  useEffect(() => {
    let mounted = true;
    if (loadUntil250th && !paginationProps.complete) {
      mounted && setLoading(true);
      let limit = 250 - paginationProps.page * BATCH_LIMIT;
      fetchListItems(paginationProps, limit, paginationProps.page * BATCH_LIMIT)
        .then((results) => {
          if (mounted) {
            setPaginationProps({
              ...paginationProps,
              page: 250 / BATCH_LIMIT,
              autoLoad: false,
              complete: paginationProps.totalItems <= 250,
            });
            setListItems(results.listItems);
            setLoading(false);
          }
        })
        .catch((err) => {
          logger.log('LOADMORE FAILED:', err);
          mounted && setPaginationProps({ ...paginationProps, complete: true });
          mounted && setLoading(false);
        });
    }
    return () => {
      mounted = false;
    };
  }, [loadUntil250th]);

它想要这个依赖数组,导致无限循环

[loadUntil250th, logger, paginationProps, setListItems]);

如果我不想要它们,我想了解为什么需要它。

【问题讨论】:

标签: reactjs use-effect


【解决方案1】:

'exhaustive-deps' lint 规则旨在防止stale closures,其中useEffect 引用回调中使用但不存在于依赖数组中的道具或状态。由于loggerpaginationPropssetListItems 理论上可以在渲染之间更改,因此在useEffect 中引用它们而不将它们包含在依赖数组中是不安全的,以确保您始终接收并执行最多-日期数据。您可以将 useEffect 视为本质上在创建时生成所有状态和道具的快照,并且在其依赖项之一发生更改时更新它。

例如,如果在依赖项列表中不包括 paginationProps,如果 fetchListItems 曾经修改过 paginationProps 的值,那么在 loadUntil250th 更改之前,useEffect 将无法访问该更新后的值。

正如this answer 中提到的,部分问题是您对useEffect() 的使用是unidiomatic。如果您所做的只是订阅对loadUntil250th 的更改,那么您最好将此函数移到别处并使用修改loadUntil250th 的代码调用它。

如果您想将代码保留在 useEffect 挂钩中,您有几个选择:

  1. 假设paginationPropssetPaginationProps 派生自useState 挂钩,您可以通过passing a function 消除对paginationProps 的依赖,而不是对象。所以你的代码会变成:
setPaginationProps(paginationProps => {
    ...paginationProps,
    page: 250 / BATCH_LIMIT,
    autoLoad: false,
    complete: paginationProps.totalItems <= 250,
});
  1. 如果可能,请移动 setListItems inside the useEffect hook。这确保您可以控制该功能所依赖的任何道具/状态。如果这是不可能的,你有几个选择。您可以将函数移出组件,以保证它不依赖于 props 或 state。或者,您可以将useCallback hook 与该函数一起使用来控制其依赖项,然后将setListItems 列为另一个依赖项。
  2. logger 函数不太可能在渲染之间发生变化,因此您可能可以安全地将其保存在依赖项数组中(尽管 linter 会期望这种情况很奇怪)。

如果您仍然好奇,this article 有助于详细说明 useEffect 和依赖数组的实际工作原理。

【讨论】:

    猜你喜欢
    • 2019-10-25
    • 1970-01-01
    • 2021-10-16
    • 2021-09-27
    • 2021-09-15
    • 2020-02-26
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多