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

时间:2019-08-22 10:26:15

标签: javascript reactjs react-hooks

处于挂起状态时,它可能会过时并泄漏内存:

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-deps ESLint规则),因此我们必须使用useCallback来维护引用,但是它会重新生成每次通话都取决于 state.number 更新

这是一个人为的示例,但是在获取数据时我遇到了很多次。有什么解决方法吗?还是在这种情况下Facebook错误?

2 个答案:

答案 0 :(得分:3)

使用状态设置器的功能形式:

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

答案 1 :(得分:0)

fetchSomething在这里不是依赖项。您不想重新触发效果,只在组件安装时才造成一次。那就是useEffect(() => ..., [])的目的。