React hooks:如何使用 react-hooks/exhaustive-deps 规则在没有无限循环的情况下读取和更新钩子中的状态

IT技术 javascript reactjs react-hooks
2021-05-24 05:10:33

当状态处于钩子中时,它可能会变得陈旧并泄漏内存:

function App() {
  const [greeting, setGreeting] = useState("hello");

  const cb = useCallback(() => {
    alert("greeting is " + greeting);
  }, []);

  return (
    <div className="App">
      <button onClick={() => cb()}>Click me</button>
      <p>
        Click the button above, and now update the greeting by clicking the one
        below:
      </p>
      <button onClick={() => setGreeting("bye")}>
        Update greeting
      </button>
      <p>Greeting is: {greeting}</p>
      <p>
        Now click the first button again and see that the callback still has the
        old state.
      </p>
    </div>
  );
}

演示:https : //codesandbox.io/s/react-hook-stale-datamem-leak-demo-9pchk

这样做的问题是,如果我们遵循 Facebook 的建议始终列出所有依赖项,并确保我们没有陈旧的数据或内存泄漏(如示例所示),我们将在典型场景中遇到无限循环来获取一些数据多于):

const [state, setState] = useState({
  number: 0
});

const fetchRandomNumber = useCallback(async () => {
  if (state.number !== 5) {
    const res = await fetch('randomNumber');
    setState(v => ({ ...v, number: res.number }));
  }
}, [setState, state.number]);

useEffect(() => {
  fetchRandomNumber();
}, [fetchRandomNumber]);

由于 Facebook 说我们应该将其fetchRandomNumber列为依赖项(react-hooks/exhaustive-depsESLint 规则),我们必须使用它useCallback来维护一个引用,但它会在每次调用重新生成,因为它既依赖于state.number更新它

这是一个人为的例子,但我在获取数据时遇到过很多次。在这种情况下,是否有解决方法或 Facebook 错误?

2个回答

使用状态设置器的函数形式:

const fetchData = useCallback(async () => {
  const res = await fetch(`url?page=${page}`);
  setData((data) => ([...data, ...res.data]));
  setPage((page) => page + 1);
}, [setData, setPage]);

现在你不需要数据和页面作为你的部门


您还可以使用 ref 仅在 mount 上运行效果:

  const mounted = useRef(false);

  useEffect(() => {
    if(!mounted.current) {
      fetchSomething();
      mounted.current = true;
    }

    return () => { mounted.current = false }
  }, [fetchSomething]);

const fetchSomething = useCallback(async () => {
  ...
}, [setData, setPage, data, page]);

fetchSomething不是这里的依赖项。您不想重新触发效果,您只会在组件安装时引发一次。useEffect(() => ..., [])是为了什么