useMemo:什么是它,计算缓存和 React Native

作者: IT Sectr 发布日期: 2026-07-05 阅读时间: 10 分钟

React Native 中的 useMemo hook 可以在组件渲染之间记忆化计算结果,防止重复执行昂贵的操作。与 useCallback 记忆化函数不同,useMemo 保存返回值,并且仅在指定的依赖项发生更改时重新计算。根据 React Docs, 2024,记忆化对于大型数组的计算和复杂的数据转换特别有效。在 React Native 中,每次额外的重新渲染都会给 JavaScript 和原生线程之间的桥接连接带来额外的负载,因此正确使用 useMemo 直接影响动画的流畅性和界面的响应速度。

要点

  • useMemo — 用于记忆化值的 React hook,在渲染之间缓存函数的结果
  • 该 hook 接受两个参数:计算函数和依赖项数组
  • 仅当至少一个依赖项发生变化时,值才会重新计算
  • 在 React Native 中,useMemo 对于优化列表和动画的性能至关重要
  • 过度使用 useMemo 可能由于比较依赖项的开销而降低性能

什么是 React Native 中的 useMemo

useMemo — 是来自 React 标准库的 hook,在 React Native 中无需额外包即可使用。它记忆化传递函数返回的值,并在后续渲染中重用该值,直到数组中指定的依赖项发生更改。

该 hook 的名称来自英语 memoization — 一种优化技术,其中昂贵函数的结果被存储在缓存中。当使用相同参数重复调用时,返回存储的值而不是重新计算。

在 React Native 的上下文中,由于架构原因,记忆化尤为重要。组件的每次重新渲染都会通过 JavaScript — 原生桥接发送数据,这需要时间。如果组件包含昂贵的计算(列表过滤、排序、格式化),每次重新渲染都会重复这些计算,阻塞 JS 线程并导致丢帧。

根据 React Native Performance Docs, 2024,瓶颈通常不是原生组件的渲染,而是渲染之间 JavaScript 逻辑的执行。useMemo 正是为了解决这个问题。

值的记忆化如何工作

useMemo 接受一个函数和一个依赖项数组,返回记忆化的值。React 存储先前的值和依赖项数组。在每次渲染时,React 使用 Object.is 将当前依赖项与先前的依赖项进行比较。如果至少有一个依赖项发生了变化,函数将重新执行并保存结果。

语法和参数传递

useMemo 的签名在 Web 和 React Native 中完全相同。第一个参数 — 一个无参数函数,返回要记忆化的值。第二个 — 依赖项数组,当这些依赖项发生变化时,值将被重新计算。

js
import React, { useMemo } from 'react';

const sortedList = useMemo(() => {
    return items.sort((a, b) => a.name.localeCompare(b.name));
}, [items]);

在此示例中,sortedList 仅在 items 数组的引用发生更改时才重新计算。如果数组内容发生更改但引用保持不变(突变),useMemo 将不会看到更改 — 这是一个重要的限制。

通过 Object.is 比较依赖项

React 使用 Object.is 算法来比较依赖项,该算法类似于严格相等 ===,但正确处理 NaN(+0 和 -0 被视为不同)。这意味着对于原始类型(字符串、数字、布尔值),比较按值进行,而对于对象和数组,则按引用进行。

js
// Object.is({'a': 1}, {'a': 1}) -> false (不同引用)
// Object.is(42, 42) -> true (原始值按值比较)
// Object.is(NaN, NaN) -> true (正确处理 NaN)

理解这一机制至关重要:如果在每次渲染时将新对象传递给依赖项,记忆化将毫无用处,因为依赖项每次都会被视作已更改。

何时使用 useMemo

useMemo 在三种情况下是合理的:昂贵的计算、向子组件传递稳定的 props 以及保持对象的引用标识。在 React Native 中,由于移动应用的架构特点,这三种情况经常出现。

昂贵的计算

如果组件执行大型数据数组的处理(过滤、排序、分组)或资源密集型数学操作,useMemo 可防止每次重新渲染时重复这些计算。在移动应用中,典型的例子是在列表显示之前格式化 API 数据。

为子组件提供稳定的 props

当子组件被包裹在 React.memo 中时,它仅在 props 发生更改时才重新渲染。如果 props 对象是在父组件内部没有 useMemo 的情况下创建的,那么每次父组件渲染时都会创建一个新的引用,子组件将重新渲染,从而使 React.memo 的好处化为乌有。

样式计算的记忆化

在 React Native 中,样式通常取决于 props — 例如,元素的宽度根据屏幕尺寸进行计算。useMemo 允许仅在输入参数发生变化时计算这些动态样式。

何时 useMemo 无效

useMemo 不是万能的优化工具。在某些情况下,它不会带来好处,甚至可能由于存储和比较依赖项的开销而降低性能。在 React Native 中,每个字节的内存都很重要,不测量效果就盲目使用 useMemo 是一种反模式。

该 hook 在三种主要情况下没有益处。首先,如果计算是微不足道的(简单的加法、字符串拼接),useMemo 的成本超过了计算本身的价值。其次,如果依赖项每次渲染都会更改 — 函数仍会执行,记忆化只会增加额外的工作。第三,如果组件很少渲染,缓存存储的开销是不合理的。

根据 Kent C. Dodds, 2023,在添加 useMemo 之前,需要使用 React DevTools Profiler 或 React Native 内置的 Performance Monitor 来测量问题。如果组件的重新渲染时间少于 1 毫秒,则不需要记忆化。

  • 微不足道的计算 — 加法、拼接、简单的三元运算
  • 罕见的重新渲染 — 只渲染一次的静态屏幕
  • 变化的依赖项 — 如果依赖项在每次渲染时都是新的,记忆化无效
  • 原始值 — 数字和字符串不需要记忆化,它们本来就很便宜

useMemo 和 useCallback:比较

useMemo 和 useCallback — 来自 React 的两个记忆化 hook,经常被混淆。区别在于它们保存什么:useMemo 返回函数执行的结果(任何值),而 useCallback 返回函数本身。两者都接受一个依赖项数组,但语法不同。

特性useMemouseCallback
返回函数执行的结果对函数的引用
第一个参数返回值的函数要记忆化的函数
典型用途缓存计算为子组件提供稳定的回调
等价性useMemo(() => fn, deps)useCallback(fn, deps) === useMemo(() => fn, deps)

在实践中,useCallback 是 useMemo 之上的语法糖,用于需要记忆化函数的情况。在 React Native 中,两个 hook 的使用频率相同:useMemo — 用于数据(格式化文本、计算样式),useCallback — 用于事件处理程序(onPress、onChangeText)。

useMemo 的实践示例

让我们来看一下 React Native 应用中使用 useMemo 的两个真实场景。第一个 — 根据搜索查询过滤联系人列表,第二个 — 根据屏幕尺寸计算动态样式。

示例 1:列表搜索和过滤

在此示例中,useMemo 可防止在每次渲染时重新过滤大型联系人列表,如果搜索查询和源数据都没有更改的话。

js
const ContactList = ({ contacts, searchQuery }) => {
    const filteredContacts = useMemo(() => {
        if (!searchQuery.trim()) return contacts;
        const query = searchQuery.toLowerCase();
        return contacts.filter((c) =>
            c.name.toLowerCase().includes(query) ||
            c.phone.includes(query)
        );
    }, [contacts, searchQuery]);

    return (
        <FlatList
            data={filteredContacts}
            renderItem={renderContact}
            keyExtractor={(item) => item.id}
        />
    );
};

没有 useMemo,过滤操作将在每次渲染时执行,包括由动画或组件其他部分状态更改引起的渲染。使用 useMemo,过滤操作仅在 contacts 或 searchQuery 更改时触发。

示例 2:计算动态样式

在 React Native 中,样式通常取决于屏幕尺寸或组件的 props。useMemo 允许一次性计算这样的样式对象,并在依赖项发生更改之前重复使用它。

js
const Card = ({ title, isSelected }) => {
    const cardStyle = useMemo(() => ({
        backgroundColor: isSelected ? '#e3f2fd' : '#ffffff',
        borderWidth: isSelected ? 2 : 1,
        padding: 16,
    }), [isSelected]);

    return (
        <View style={cardStyle}>
            <Text>{title}</Text>
        </View>
    );
};

这种方法确保 cardStyle 对象在 isSelected 更改之前保持稳定(相同引用)。如果 Card 被包裹在 React.memo 中,这种模式可以防止子元素的不必要重新渲染。

常见问题

useMemo 返回什么?

useMemo 返回记忆化的值 — 作为第一个参数传递的函数执行的结果。在后续渲染中,如果依赖项没有更改,React 返回存储的值。

useMemo 和 useCallback 有何不同?

useMemo 返回函数执行的结果(任何值:数字、对象、JSX),而 useCallback 返回函数本身。此外,useCallback(fn, deps) 等价于 useMemo(() => fn, deps)。

何时不需要 useMemo?

useMemo 在微不足道的计算(加法、拼接)、罕见的重新渲染、每次渲染都变化的依赖项以及原始类型的情况下是无效的。在添加 useMemo 之前始终要测量性能。

useMemo 会降低性能吗?

是的,如果不需要使用的话。React 将先前的值存储在内存中,并在每次渲染时执行依赖项比较。对于简单的计算,这些操作的开销超过了记忆化的收益。

如何调试 useMemo 的工作?

使用 React DevTools Profiler 来测量组件的渲染时间。在 React Native 中,还可以通过开发者菜单使用 Performance Monitor。如果重新渲染时间少于 1 毫秒 — 则不需要 useMemo。

总结

  • useMemo — 用于记忆化值的 React hook,在渲染之间缓存函数的结果
  • 该 hook 接受计算函数和依赖项数组,仅在它们更改时重新计算值
  • 在 React Native 中,useMemo 对于优化列表、动画和复杂计算尤为重要
  • 关键场景:昂贵的计算、为 React.memo 提供稳定的 props、动态样式
  • useMemo 与 useCallback 不同:前者返回值,后者返回函数
  • 过度使用 useMemo 会由于存储和比较依赖项的开销而降低性能
  • 在添加 useMemo 之前,始终通过 React DevTools Profiler 或 React Native 中的 Performance Monitor 测量问题

我们将开发一款交钥匙移动应用程序

IT Sectr自2017年以来为初创企业和企业打造iOS和Android应用程序。我们将为您提供咨询并提出最佳解决方案。

讨论项目

另请阅读