欢迎光临散文网 会员登陆 & 注册

React手册 Hooks 之 useMemo

2023-05-17 18:12 作者:海里我最大  | 我要投稿

描述

    React 官网对 useMemo 的描述原文

    useMemo is a React Hook that lets you cache the result of a calculation between re-renders.

useMemo 是一个 React Hook 可以在你重新渲染之间缓存一个计算结果.

    可以看出 useMemouseCallback 非常类似, 都是起到缓存优化的作用, 区别在于 useMemo 用来缓存计算结果, 而 useCallback 用来缓存函数定义, 不过这只是 React 官方给出的解释, 如果你看过这两个方法的源码, 你会发现其实这两个方法也是可以混用的, useMemo 也可以缓存函数, 而 useCallback 也可以用来缓存变量或计算结果, 只不过这不符合 React 的预期(主要因为 React 认为一个匿名函数返回另一个匿名函数的定义看起来很笨重[确实]), 对后续的 React 更新并不友好, 所以 React 提出了一个专门针对函数的 useCallback.


场景

    useMemo 是为了解决 React.memo 包装组件之后, 每次渲染父组件时, 给子组件传递的变量始终是全新的对象, 导致 React.memo 优化失效的问题, 它会在父组件外部缓存变量, 当父组件渲染时, 会返回缓存的变量, 让 React.memo 能拿到相同的变量引用, 从而让优化生效.

    useMemouseCallback 一样, 应该只用做性能优化, 如果你的代码没有它之后无法正常工作, 那就应该修复潜在问题, 然后用 useMemouseCallback 提高性能.

    接口定义:


参数

  • calculateValue: Function

        计算需要缓存的值的函数, 应该是纯函数且不带有参数, 返回任意类型的值, React 会在初始渲染阶段调用这个函数, 并将结果缓存, 当 dependencies 发生变化时, 会再次调用并更新缓存中的结果, 然后从 useMemo 返回.

  • dependencies: Array<mixed>

        calculateValue 函数中引用的所有触发更新的值的数组集合, 能够触发的值包括 props, state 以及直接在组件主体内声明的所有变量和函数. 这个数组的长度应该是固定的, 每次渲染时, React 会对数组内的值进行 Object.is 判断, 如果发生变化则重新计算新值, 如果没有变化, 则返回缓存中的值. 如果没有传递该值, 那么会导致 useMemo 始终返回新值, 如果传递空数组[], 那么 useMemo 会始终缓存该值.

返回

    初始渲染时, useMemo 会返回 calculateValue 函数的返回值, 后续渲染中它会先判断依赖项是否发生变化, 如果变化就重新使用 calculateValue 计算新值并缓存, 如果没有变化, 那么返回缓存中的值.


用法1

    跳过昂贵的重新计算, 要在重新渲染之间缓存计算结果, 可以像下面这样使用 useMemo

    当 keywordtime 没有发生变化, 那么 useMemo 始终返回相同的结果, 这样可以跳过不必要的计算, 如果 filterBigList 的计算量很大的是时候, 效果会非常明显, 这样的缓存被称为记忆化(Memoization).

    如果只在首次渲染时计算, 并且没有任何依赖项目, 代码看起来像这样: useMemo(() => { ... }, []); 也就是说计算之后希望能一直缓存结果, 那么可以使用 useStateuseRef, 这种情况下可能 useMemo 并不是最优的选择, 原因是 React 官方给出的解释, 在未来的 React 规划中, 存在丢弃缓存的设计, 缓存的目的是为了跳过多余的渲染, 在一些特定的, 一定不会渲染的情况时, 缓存就是多余的, 如果你只是用 useMemo 来做优化, 这并没有什么影响, 但是如果担心丢失缓存对业务有影响, 那么可以使用 useStateuseRef 来保证缓存.

    

用法2

    跳过组件的重新渲染, 在默认情况下, 一个组件重新渲染时, React 会递归地重新渲染它的所有子组件, 这在大多数时候都是不必要的, 所以需要使用 React.memo 方法将组件包装, 包装后的组件, 在 props 没有变化的时候, 不会再进行重新渲染,

    在例子中, 使用 React.memo 包装了子组件 Children 同时 Children 接收一个 data={childrenData}props, 此时会发现 React.memo 并没有生效, 每次 MyApp 重新渲染时, 子组件 Children 依然会跟着渲染, 原因其实是 React.memo 的优化只有在 props 没有变化时才会生效, 而在例子中传递的 childrenData, 在每次重新渲染的时候, 都会获得一个全新的对象引用, 这样会导致 React.memoprops 进行 Obejct.is 对比的时候, 始终不会像等, 这样 React.memo 的优化就会失效.

    所以需要使用下面这样使用 useMemo 来让 React.memo 优化生效


用法3

    使用 useMemo 包装 JSX 节点, 因为 JSX 节点可以看作是一个类似 { type: "List", props: { ... } } 这样的对象, 那么如果是对象就可以被 useMemo 缓存(记忆).

    这样的用法实际上和使用 const ListMemo = React.memo(List); 是一样的, 都是当 name 发生变化后重新渲染 List 组件, 区别在于 React.memo 可以指定一个判断函数(React.memo 的第二个参数), 而不仅仅是根据 props 进行浅比较, 而 useMemo 只能控制传递进去的 dependencies 相对而言没有 React.memo 灵活, 所以更推荐使用 React.memo 直接包装组件.


总结

  • useMemo 是一个可以将值记忆化(Memoization) 的一个 hook;

  • useMemo 需要谨慎使用, 管理好 dependencies 是优化的关键;

  • useMemo 不应该用来实现功能, 只用来做优化;

  • dependencies 不传会始终返回新值, 传空数组会在渲染一次之后一直缓存;

  • 如果希望值在一次计算之后一直缓存, 没有依赖项目, 考虑使用 useStateuseRef 代替;

  • useMemouseCallback 在实现上并没有什么区别, 只是调用的方式不同;

  • 虽然 useMemo 可以缓存 JSX 节点, 但是不推荐, 优先使用 React.memo 缓存组件.



React手册 Hooks 之 useMemo的评论 (共 条)

分享到微博请遵守国家法律