likes
comments
collection
share

什么时候用 useMemo、useCallback?分享自己的看法与 React 新文档的说法

作者站长头像
站长
· 阅读数 75

前言

react hooks 推出以来,useMemouseCallack 几乎就被所有人诟病,对新手更是不友好。什么时候应该用,什么时候不用?用了的话,依赖性一多,可读性就会很差,甚至可能出现 bug。每个人都有自己的理解,看过挺多文章,无论怎么用,多用还是少用,都有不同的看法。

个人观点

这类文章看了很多,国内外的都有,再加上我自己的理解和日常使用情况。

useMemo

一般两种情况下我会用。

  • 计算量比较大(怎么样才算下面会讲到,这里不举例子)
  • 计算量可能不大,但需要分成很多步才能得到一个结果值。(举个例子)
// 不用 useMemo,可读性不高,代码都在同一级,可能不容易看出只需要用到 data
function App() {
  const a = ...;
  const b = ...;
  const c = ...;
  const data = ...用上面这些变量来得到;
  
  return <div>{data.map(...)}</div>
}

// 用 useMemo,可读性增加,代码结构化
function App() {
  const data = useMemo(() => {
      const a = ...;
      const b = ...;
      const c = ...;
      const result = ...用上面这些变量来得到;
      return result;
  }, [...])
  
  return <div>{data.map(...)}</div>
}

useCallback

遇到性能问题的时候再用,并且只有当函数有传给子组件的时候,并且子组件有用 React.memo 包裹才会有效果。如果你没有传给子组件,或根本没有子组件,不要滥用!依赖项一多,可读性很差。

比如你用 antd 的 Button 传入一个 onClick,并用 useCallback 包裹,是没用的,因为 antd 的 Button 没有包裹 React.memo。除非你用 useMemo 包裹这个 Button,不过没有这个必要。

现代浏览器和设备,99% 的情况下都不会有性能问题,除非一些极少的特定情况,如果有性能问题,开发的时候就会遇到了,这时候再来做这个优化也不迟。

React 新文档

链接,这里只挑选一部分重点进行翻译,详细的还是看原文比较好。

useMemo

怎么判断计算昂不昂贵

一般而言,除非您要创建或遍历数千个对象,否则它可能并不昂贵。你可以添加一个控制台日志来衡量一段代码所花费的时间:

console.time('filter array');  
const visibleTodos = filterTodos(todos, tab);  
console.timeEnd('filter array');
console.time('filter array');  
const visibleTodos = useMemo(() => {  
  return filterTodos(todos, tab); // Skipped if todos and tab haven't changed  
}, [todos, tab]);  
console.timeEnd('filter array');

useMemo 不会使第一次渲染更快。它只会帮助您跳过不必要的更新工作。

应该在所有地方添加 useMemo 吗?

优化useMemo 只在少数情况下有价值:

  • 你要放在 useMemo 中的计算明显很慢,并且它的依赖关系很少发生变化。
  • 你将其作为 prop 传递给 memo 包装的组件。如果值没有发生变化,你想跳过重新渲染。记忆化可以让你的组件仅在依赖项不同的情况下重新渲染。
  • 你传递的值后来被用作某个 Hook 的依赖项。例如,另一个 useMemo 计算值可能依赖于它。或者你可能从 useEffect 依赖于这个值。

在实践中,您可以通过遵循一些原则来避免大量记忆:

  • 当一个组件包装其他组件时,让它接受 JSX 作为 children。这样,当包装组件更新自己的状态时,React 知道其子组件不需要重新渲染。
  • 优先使用本地状态,不要将状态向上提升得比必要的更高。例如,不要在树的顶部或全局状态库中保留短暂状态,例如表单和是否悬停的项。
  • 保持你的渲染逻辑纯粹。如果重新渲染组件会导致问题或产生一些明显的视觉瑕疵,则这是你组件中的一个 bug!修复 bug 而不是添加记忆化。
  • 避免不必要的更新状态的 Effects。React 应用程序中大多数性能问题都是由 Effects 导致的更新链引起的,这会导致组件一遍又一遍地重新渲染。
  • 尝试从 Effects 中删除不必要的依赖项。例如,在 Effect 内部或组件外部移动某些对象或函数通常比记忆更简单。

useCallback

与 useMemo 有什么关系?

  • useMemo 缓存调用函数的结果
  • useCallback 缓存函数本身

你应该在所有地方添加 useCallback 吗?

缓存函数useCallback 仅在少数情况下有价值:

  • 您将它作为 prop 传递给包装了 memo 的组件。你想跳过重新渲染在这个值没有发生变化的时候。记忆让你的组件只有在依赖关系发生变化时才重新渲染。
  • 您传递的函数稍后用作某些 Hook 的依赖项。例如,另一个包裹了 useCallback 的函数依赖于它,或者你的 useEffect 依赖于这个函数。

useCallback不会阻止创建函数。render 时总是会创建一个函数,但 React 会忽略它并在没有任何改变的情况下返回一个缓存的函数。

在实践中,您可以通过遵循一些原则来避免大量记忆:

useMemo 相同

个人感想

必须使用的情况

  • 计算量特别大的时候,像文档所说,创建或遍历数千个对象,要使用 useMemo。其它情况用不用看个人习惯。
  • 子组件重新渲染造成的影响很大的时候,子组件包裹 memo,并且传递给子组件的函数用 useCallback 包裹。否则使用 useCallback 几乎没有作用。
  • 你开发了一个类似 react-useahooks 的 npm 包,返回的函数方法要包裹 useCallback,使用的人可能需要做优化。我就遇到过这种情况,准备给库提 pr,然后发现最新版本加了。

把新文档看了下来,觉得我的个人观点和使用,是没有什么问题的。看过挺多 ui 库的源码,也贡献过不少次代码,其实也都是差不多的,没有无脑用。从 react-labs 3月报告 来看 react-forgot 已经在 facebook 内部试用了,希望能早点稳定然后开源吧,这样我们就不用再纠结这些了。

转载自:https://juejin.cn/post/7215824615648804921
评论
请登录