3 回答
TA贡献2016条经验 获得超9个赞
为了从useEffect回调内部监听 state 的变化(当你没有关注任何 props 更新时),你可以将 state 保存在组件范围之外的变量中,并直接使用它而不是 state。
代码如下:
import React, { useState, useEffect } from "react";
import ReactDOM from "react-dom";
const debounce = (func, wait, immediate) => {
let timeout;
return function() {
const context = this;
const args = arguments;
clearTimeout(timeout);
timeout = setTimeout(() => {
timeout = null;
if (!immediate) func.apply(context, args);
}, wait);
if (immediate && !timeout) func.apply(context, args);
};
};
let isFetchingState;
const App = () => {
const [isFetching, setIsFetching] = useState(false);
isFetchingState = isFetching;
const handleScroll = debounce(() => {
setIsFetching(!isFetchingState);
console.log({ isFetchingState });
}, 300);
useEffect(() => {
window.addEventListener("scroll", handleScroll);
return () => window.removeEventListener("scroll", handleScroll);
}, []);
return <div style={{ height: "1280px" }}>Hello world</div>;
};
const root = document.getElementById("root");
if (root) ReactDOM.render(<App />, root);
TA贡献1872条经验 获得超3个赞
添加isFetching为依赖项useEffect
虽然我无法提供深入的解释,但我可以说,useEffect当您说效果不依赖于任何东西时,您基本上是在向 React 撒谎,通过提供一个空的依赖项数组,传递包含在您中的所有变量总是好的影响。
此外,您每次组件时都会创建一个新函数re-render,以避免将函数移动到内部useEffect或将其包装在内部useCallback,除非依赖项数组中的某些内容发生更改,否则不会创建重新创建函数
useEffect(
() => {
const handleScroll = debounce(() => {
setIsFetching(prevState => !prevState);
console.log({ isFetching });
}, 300);
window.addEventListener("scroll", handleScroll);
return () => window.removeEventListener("scroll", handleScroll);
},
[isFetching]
);
或与 useCallback
const handleScroll = useCallback(
debounce(() => {
setIsFetching(prevState => !prevState);
console.log({ isFetching });
}, 300),
[isFetching]
);
useEffect(
() => {
window.addEventListener("scroll", handleScroll);
return () => window.removeEventListener("scroll", handleScroll);
},
[isFetching]
);
TA贡献1820条经验 获得超10个赞
您已将一个空数组作为第二个参数传递给 useEffect。这就是您在运行后不再被称为 useEffect 的关键问题。
要解决此问题,请不要传递第二个参数。
useEffect(() => {
window.addEventListener("scroll", handleScroll);
return () => window.removeEventListener("scroll", handleScroll);
});
或者,每当属性被更改时调用 useEffect :
useEffect(() => {
window.addEventListener("scroll", handleScroll);
return () => window.removeEventListener("scroll", handleScroll);
}, [isFetching]); // re-run useEffect on isFetching changed
这类似于我们在 componentDidUpdate 中所做的:
if (prevState.count !== this.state.count) {
// do the stuff
有关更多详细信息,请参阅文档本身。
文档中的注释:
如果您只想运行效果并仅将其清理一次(在装载和卸载时),您可以传递一个空数组 ([]) 作为第二个参数。这告诉 React 你的 effect 不依赖于来自 props 或 state 的任何值,所以它永远不需要重新运行。这不是作为特殊情况处理的——它直接遵循依赖项数组的工作方式。
如果传递一个空数组 ([]),则效果中的 props 和 state 将始终具有其初始值。虽然传递 [] 作为第二个参数更接近熟悉的 componentDidMount 和 componentWillUnmount 心智模型,但通常有更好的解决方案来避免过于频繁地重新运行效果。另外,不要忘记 React 将 useEffect 的运行推迟到浏览器绘制完成之后,所以做额外的工作不是问题。
添加回答
举报