React hooks:如何阅读 &使用 react-hooks/exhaustive-deps 规则更新没有无限循环的钩子中的状态 [英] React hooks: How to read & update state in hooks without infinite loops with react-hooks/exhaustive-deps rule

查看:36
本文介绍了React hooks:如何阅读 &使用 react-hooks/exhaustive-deps 规则更新没有无限循环的钩子中的状态的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

function App() {const [问候,setGreeting] = useState("hello");const cb = useCallback(() => {alert("问候语是" + 问候语);}, []);返回 (<div className="应用程序"><button onClick={() =>cb()}>点击我</button><p>单击上面的按钮,现在通过单击更新问候语以下:</p><button onClick={() =>setGreeting("再见")}>更新问候语<p>Greeting 是:{greeting}</p><p>现在再次点击第一个按钮,看到回调仍然有旧状态.</p>

);}

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

这样做的问题是,如果我们按照 Facebook 的建议始终列出所有依赖项,并确保我们没有陈旧的数据或内存泄漏(如上面的例子):

const [state, setState] = useState({数量: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-deps ESLint 规则),我们必须使用 useCallback维护一个引用,但它在每次调用时重新生成,因为它既依赖于 state.number更新它.

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

解决方案

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

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 mount = useRef(false);useEffect(() => {如果(!mounted.current){fetchSomething();mount.current = true;}返回 () =>{mounted.current = false }}, [fetchSomething]);

还有

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

When state is in a hook it can become stale and leak memory:

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>
  );
}

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

The problem with that is that we will run into infinite loops in a typical scenario to fetch some data if we follow Facebook's advice to list all dependencies always, as well as ensure we don't have stale data or memory leaks (as the example showed above):

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]);

Since Facebook say we should list fetchRandomNumber as a dependency (react-hooks/exhaustive-deps ESLint rule) we have to use useCallback to maintain a reference, but it regenerates on every call since it both depends on state.number and also updates it.

This is a contrived example but I've run into this many times when fetching data. Is there a workaround for this or is Facebook wrong in this situation?

解决方案

Use the functional form of the state setter:

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

Now you don't need data and page as your deps


You can also use a ref to run the effect only on mount :

  const mounted = useRef(false);

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

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

And

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

这篇关于React hooks:如何阅读 &amp;使用 react-hooks/exhaustive-deps 规则更新没有无限循环的钩子中的状态的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆