什么时候应该用useCallback中代码优化

28 阅读3分钟

上一篇文章:什么时候应该用useCallback

你好!我看到你对 Canvas 中 MyDataFetcher 组件的代码有疑问。你提出的问题非常棒,它触及了 useEffectuseCallback 的一个关键点。


关于 useEffect 的优化和依赖项

你问到 useEffect 的第二个参数能否使用空数组 []。答案是可以,而且在某些情况下这是更好的选择。

让我们来分析一下两种情况的区别:

  1. 使用 [fetchData] 作为依赖项:

    • 优点:代码的意图非常清晰。useEffect 明确声明它依赖于 fetchData 这个函数。因为我们用 useCallback 包装了 fetchData,所以 fetchData 的引用是稳定的,useEffect 只会在组件挂载时运行一次。这是最安全推荐的做法,因为它遵循了 React Hooks 的依赖项规则。
  2. 使用 [] 作为依赖项:

    • 优点:这也可以达到只在组件挂载时运行一次的效果。当 useEffect 的依赖项为空数组时,React 知道这个副作用不依赖于任何 propsstate,所以只会在组件首次渲染后执行一次,后续的重新渲染不会触发它。
    • 缺点:在 ESLintreact-hooks/exhaustive-deps 规则下,这会触发一个警告。这个警告会提示你,因为你在 useEffect 内部使用了 fetchData 函数,所以它应该被包含在依赖项数组中。虽然在这个特定的例子中,因为 fetchData 依赖于空数组,不会引发问题,但这个警告是为了防止你犯错:如果 fetchData 依赖于组件的 stateprops,但你将 useEffect 的依赖项设置为 [],那么 useEffect 内部使用的 fetchData 将会是一个“旧”的、不包含最新 stateprops 的闭包,从而导致 bug。

因此,为了遵循 React 的最佳实践并避免潜在的闭包问题,将函数作为依赖项并用 useCallback 缓存它是一种更健壮、更“正确”的写法。


如何优化代码?

你提供的代码已经是一个很好的例子,展示了 useCallback 的正确用法。不过,我们可以从可读性和简洁性的角度进行一些微调。

优化方案:将 fetchData 函数内联到 useEffect

在许多情况下,如果一个函数只在一个 useEffect 中被使用,你可以直接将它定义在 useEffect 内部,而不是用 useCallback 把它提升到组件作用域。

JavaScript

import React, { useState, useEffect } from 'react';

function MyDataFetcher() {
  const [data, setData] = useState(null);

  useEffect(() => {
    // 将函数内联到 useEffect 内部
    const fetchData = async () => {
      try {
        const response = await fetch('https://api.example.com/data');
        const result = await response.json();
        setData(result);
      } catch (error) {
        console.error("数据获取失败", error);
      }
    };

    fetchData();
  }, []); // 依赖项数组为空,表示只在组件挂载时运行一次

  return <div>{data ? JSON.stringify(data) : '正在加载...'}</div>;
}

为什么这种方案也行得通?

  • 当函数被内联到 useEffect 内部时,它会成为 useEffect 闭包的一部分。
  • 因为 useEffect 的依赖项是空数组 [],所以整个副作用函数只会在组件挂载时被创建和运行一次。
  • 这种写法更简洁,因为它省去了 useCallback。它清晰地表明,fetchData 这个函数只与这个特定的副作用相关。

总结

  • 在你的原始代码中,使用 useCallback 是一个很好的做法,它确保了 useEffect 的依赖项是稳定的。
  • 另一种简洁的优化是:如果一个函数只在一个 useEffect 中被使用,你可以考虑将其内联useEffect 内部,并使用空数组 [] 作为依赖项。

这两种写法都可以实现你的目标,但内联的写法在某些情况下更具可读性。你可以根据自己的编码风格和项目需求来选择。

希望这能解答你的疑问!