反应:在有效的时间间隔内缺少依赖警告

Posted

技术标签:

【中文标题】反应:在有效的时间间隔内缺少依赖警告【英文标题】:React: missing dependency warning within interval in effect 【发布时间】:2021-10-03 17:29:38 【问题描述】:

我有一个用 ReactNext.js 构建的反应页面,看起来像这样。

import Head from 'next/head';
import  useEffect, useState  from 'react';
import Header from '../components/Header';

export default function Home() 
    const ticksPerSecond = 10;

    const [points, setPoints] = useState(10);
    const [pointsPerSecond, setPointsPerSecond] = useState(2);

    useEffect(() => 
        setInterval(() => 
            setPoints((points) => points + pointsPerSecond / ticksPerSecond);
        , 1000 / ticksPerSecond);
    , []);

    return (
        <>
            <Header points=points pointsPerSecond=pointsPerSecond />
        </>
    );

此代码按预期工作。每秒points 将增加pointsPerSecond,状态将每秒更新10 次(由ticksPerSecond 确定)。

我的问题来自 eslint,它警告我:

React Hook useEffect has a missing dependency: 'pointsPerSecond'. Either include it or remove the dependency array. You can also replace multiple useState variables with useReducer if 'setPoints' needs the current value of 'pointsPerSecond'.eslintreact-hooks/exhaustive-deps

阅读该警告后,我尝试了许多不同的解决方案来安抚天空中的 eslint 众神,但我能做到的最好是用不同的警告替换警告。具体来说,我已经尝试了来自this question 的解决方案,看起来同样的问题。但是,这些解决方案都没有抑制警告。

【问题讨论】:

pointsPerSecond 是否会在某个时候发生变化?为什么它是有状态的? @Nick 是的,它将作为应用程序使用的一部分进行更改。尚未实施 ticksPerSecond 永远不会改变吗? @Nick ticksPerSecond 是常数。 【参考方案1】:

尝试在依赖数组上添加 pointsPerSecond

  useEffect(() => 
    setInterval(() => 
      setPoints((points) => points + pointsPerSecond / ticksPerSecond);
    , 1000 / ticksPerSecond);
  , [pointsPerSecond]);

或者你可以忽略警告使用 // eslint-disable-next-line react-hooks/exhaustive-deps

 useEffect(() => 
    setInterval(() => 
      setPoints((points) => points + pointsPerSecond / ticksPerSecond);
    , 1000 / ticksPerSecond);
  // eslint-disable-next-line react-hooks/exhaustive-deps
  , []);

【讨论】:

当pointsPerSecond改变时,这不会开始另一个间隔吗? 在这种情况下,您可以使用 // eslint-disable-next-line react-hooks/exhaustive-deps 来禁用警告【参考方案2】:

我建议将pointsPerSecond 添加到依赖数组中,然后确保从效果中返回一个清理函数,以清除现有间隔。这假设您想要停止任何现有间隔并在pointsPerSecond 更改时开始一个新间隔。如果您没有在依赖项数组中包含 pointsPerSecond,那么您将使用该依赖项的陈旧版本。

import Head from 'next/head';
import  useEffect, useState  from 'react';
import Header from '../components/Header';

const ticksPerSecond = 10;

export default function Home() 
    const [points, setPoints] = useState(10);
    const [pointsPerSecond, setPointsPerSecond] = useState(2);

    useEffect(() => 
        const interval = setInterval(() => 
            setPoints((points) => points + pointsPerSecond / ticksPerSecond);
        , 1000 / ticksPerSecond);

        return () =>  clearInterval(interval) 
    , [pointsPerSecond]);

    return (
        <>
            <Header points=points pointsPerSecond=pointsPerSecond />
        </>
    );

有几个原因我几乎总是建议不要为钩子依赖数组消除 linting 错误:

您几乎总是希望您的钩子具有最新的值。 如果您将依赖项数组的 lint 警告设为静音,那么您将不会收到有关 future 依赖项的警告,而您可能确实希望收到警告。

【讨论】:

因为 pointsPerSecond 可以改变,这不会导致两个区间同时运行吗? 没有。 useEffect钩子返回的清理函数清除之前的区间【参考方案3】:

我会做一些改变。

    不要忘记在 unmouted 上清除 Interval, 在调用 setState 之前进行挂载检查。 添加 esLint 警告您的依赖项。

下面是一个例子。

useEffect(() => 
  let mounted = true;
  const i = setInterval(() => 
    if (mounted) setPoints((points) => points + pointsPerSecond / ticksPerSecond);
  , 1000 / ticksPerSecond);
  return () =>  clearInterval(i); mounted = false; 
, [pointsPerSecond]);

【讨论】:

这很有趣,mounted 变量提供了清除间隔所没有的功能? 因为 pointsPerSecond 可以改变,这不会导致两个区间同时运行吗? @JulianLachniet 不,这就是我们做 clearInterval 的原因。 @Nick 只是以防万一在卸载后间隔仍然触发,它通常不会导致问题,除了从 React 在控制台中收到关于在未安装的组件上调用 setState 的警告。对于像这样微不足道的事情,这可能不是问题,但是任何形式的异步代码我现在都只是做一个安装检查,我只是不喜欢控制台混乱。 :)。

以上是关于反应:在有效的时间间隔内缺少依赖警告的主要内容,如果未能解决你的问题,请参考以下文章

如何根据关键和时间间隔有效地查找父记录?

反应 | React Hook useEffect 缺少依赖项

以最有效的方式获取间隔期间的事件数

泊松分布

根据 30 秒内的时间间隔删除重复项

以固定的时间间隔更新图形/绘图