为什么 React 希望我添加 setter 作为 useEffects 依赖项

Why React expects me to add setters as useEffects dependencies

我正在尝试在 useEffect 回调中使用异步函数。

有一个我不明白的行为(在我的例子中与 lodash 的 throttle 函数有关)。

我不明白发生了什么,也不知道如何解决,这里是代码示例:

import throttle from 'lodash/throttle';

const myRequestWrapped = throttle(myRequest, 300);

const [name, setName] = useState('');

useEffect(() => {
    myRequest(name) // No warning

    myRequestWrapped(name); // React Hook useEffect has a missing dependency: 'myRequestWrapped'. Either include it or remove the dependency array

}, [ name ]);

如果我添加myRequestWrapped作为依赖,我有一个无限循环(效果被连续触发)。

我想 throttle 方法与计时器一起使用,它 returns 每个 运行 都有不同的结果,所以我可以理解为什么无限循环。

但我真的不明白为什么 React 想要它作为依赖项(尤其是它在不添加它的情况下工作!)。

这是什么逻辑? 为什么是 myRequestWrapped 而不是 myRequest? 我应该忽略警告还是你知道解决这个问题的干净方法?

谢谢。

不是 React 希望您将 myRequestWrapped 添加为依赖项,而是它的 eslint.

您还必须注意,ESLint 并不知道程序员的意图,因此它只会在出现错误范围时警告用户。

Hooks 严重依赖于闭包,有时很难找出与闭包相关的错误,这就是为什么 eslint 会在 useEffect 中使用可能反映更新值的变量函数时提示。

当然检查并不完美,您可以仔细决定是否需要添加对 useEffect 的依赖。

如果你看到你写的完全正确。您可以禁用警告

useEffect(() => {
    myRequest(name);

    myRequestWrapped(name);

// eslint-disable-next-line react-hooks/exhaustive-deps
}, [ name ]);

此外,你不能在功能组件直接渲染中使用油门功能,因为如果它设置状态,它不会有效,因为它的引用会改变

这里的解决方法是use useCallback hook。 Post 即使您将 myRequestWrapped 添加为 useEffect 的依赖项,您也不会看到无限循环,因为该函数只会创建一次,因为 useCallback 会记忆并且 return 每个渲染器上相同的函数引用。

但是你必须再次小心添加对 useCallback

的依赖
import throttle from 'lodash/throttle';


const Comp = () => { 
    const myRequestWrapped = useCallback(throttle(myRequest, 300), []);

    const [name, setName] = useState('');

    useEffect(() => {
        myRequest(name);
        myRequestWrapped(name);

    }, [ name ]);
    ...
}

Shubham 是对的:它不是 REACT,而是 ESLint(或 TSLint,取决于您使用的 Linter)。

如果我可以补充,Linter 建议您添加 myRequestWrapped 的原因是闭包在 JavaScript.
中的工作方式 为了让您理解,举这个更简单的例子(我需要这个,因为我需要知道里面有什么 myRequestWrapped:

const MyComp = props => {
  const [count, setCount] = React.useState(0);

  const handleEvent = (e) => {
    console.log(count);
  }

  React.useEffect(() => {
    document.body.addEventListener('click', handleEvent);

    return () => { document.body.removeEventListener('click', handleEvent); }
  }, []);

  return <button onClick={e => setCount(s => s +1)}>Click me</button>
}

所以,现在,当 MyComp 挂载时,事件监听器被添加到 document.body,并且任何时候 click 事件被触发,你调用 handleEvent, 这将记录 count.

但是,由于事件侦听器是在组件挂载时添加的,因此 handleEvent 中的变量 count 永远等于 0:这是因为添加事件侦听器时创建的 handleEvent 实例只是一个,即与事件侦听器关联的实例。

相反,如果您这样写 useEffect

React.useEffect(() => {
  document.body.addEventListener('click', handleEvent);

  return () => { document.body.removeEventListener('click', handleEvent); }
}, [handleEevent]);

每当 handleEvent 方法更新时,您的事件侦听器也会更新 handleEvent,因此当单击 document.body 时,您将始终记录最新的 [=18] =]值。