为什么 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] =]值。
我正在尝试在 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] =]值。