返回

React新文档:不要滥用useEffect哦

前端

React:useEffect 与性能优化

大家好,我是卡颂。你或你的同事在使用 useEffect 时有没有发生过以下场景:

  1. 当你希望状态 a 变化后发起请求,于是你使用了 useEffect
useEffect(() => {
  const fetchData = async () => {
    const response = await fetch('https://example.com/api/data');
    const data = await response.json();
    setState({ data });
  };
  fetchData();
}, [a]);

这段代码运行符合预期,上线后也没问题。

  1. 随着需求不断迭代,你又希望状态 b 变化后也发起请求,于是你添加了第二个 useEffect
useEffect(() => {
  const fetchData = async () => {
    const response = await fetch('https://example.com/api/data');
    const data = await response.json();
    setState({ data });
  };
  fetchData();
}, [a, b]);

你认为这个代码也应该运行良好,但事实是,这个 useEffect 每次都会触发,无论 a 还是 b 哪个发生变化。这导致了不必要的请求和性能问题。

为什么会出现这种问题?

要理解这个问题,我们需要从 useEffect 的内部实现入手。

useEffect 的内部实现

useEffect 是一个 React Hook,它允许你执行副作用操作,例如在组件挂载后发起请求、在组件卸载前进行清理工作等。

useEffect 的实现原理是利用 React 的 Diff 算法。当组件更新时,React 会比较新旧组件的 useEffect 数组。如果数组内容发生变化,React 就会调用 useEffect 回调函数,执行副作用操作。

在这个例子中,问题出在第二个 useEffect 的依赖数组 [a, b] 上。由于 ab 都是组件的状态,所以每次组件更新时,[a, b] 都会发生变化,导致 useEffect 回调函数每次都会被调用。

解决方法

为了解决这个问题,我们需要让 useEffect 仅在 a 变化时触发。我们可以使用一个中间变量 prevA 来存储上一次 a 的值,然后在 useEffect 中比较 aprevA 的值,只有当 a 发生变化时才执行副作用操作。

const prevA = useRef(a);
useEffect(() => {
  if (prevA.current !== a) {
    const fetchData = async () => {
      const response = await fetch('https://example.com/api/data');
      const data = await response.json();
      setState({ data });
    };
    fetchData();
  }
  prevA.current = a;
}, [a]);

这个解决方案可以确保 useEffect 仅在 a 变化时触发,从而避免了不必要的请求和性能问题。

总结

在使用 useEffect 时,我们需要谨慎选择依赖数组,以避免不必要的副作用操作。否则,可能会导致性能问题。

在实际项目中,我们还可以通过以下方法来优化 useEffect 的性能:

  1. 使用 useCallbackuseMemo 来避免不必要的函数和对象重新创建。
  2. 使用 useEffect 的第二个参数 cleanup 函数来进行清理工作,避免内存泄漏。
  3. 在某些情况下,我们可以使用 useReducer 来代替 useEffect,以获得更好的性能。

我希望这篇文章对你有帮助。如果你有任何问题,请随时留言。