useCreation 与 useMemo

ahooks 里,为什么?

useCreation 是 useMemo 或 useRef 的替代品。
因为 useMemo 不能保证被 memo 的值一定不会被重新计算

你的理解基本是对的,但需要更精确地解释。

在 ahooks 中,useCreation 确实被设计为 useMemo 的更可靠替代品,主要是为了解决 useMemo 在 React 未来可能被“抛弃”(比如在并发渲染、内存回收等场景下)而导致缓存值被意外重新计算的问题


为什么 useMemo 不可靠?

React 官方文档明确指出:

useMemo 是一个性能优化手段,而不是语义保证。

在未来,React 可能会“忘记”某些 memoized 值,在下次渲染时重新计算它们,尤其是在内存压力大或组件被挂起/恢复(如 Suspense、Concurrent Mode)的情况下。

这意味着即使依赖项没有变化,useMemo 的计算函数 仍有可能被重新执行。如果这个计算过程有副作用(如创建实例、绑定事件、初始化资源等),就可能导致意料之外的问题,比如:

  • 频繁创建新对象(破坏引用相等性)
  • 重复初始化 expensive 的对象(如 WebSocket、数据库连接)
  • 导致子组件不必要的 rerender

useCreation 如何解决这个问题?

useCreation 的核心思想是:确保在依赖不变的情况下,值只创建一次,绝不重新计算

其实现通常类似这样(简化版):

function useCreation<T>(factory: () => T, deps: DependencyList) {
  const isFirst = useRef(true);
  const depsRef = useRef<DependencyList>([]);
  const valueRef = useRef<T>();

  if (
    isFirst.current ||
    !depsRef.current ||
    deps.some((dep, i) => !Object.is(dep, depsRef.current[i]))
  ) {
    valueRef.current = factory();
    depsRef.current = deps;
    isFirst.current = false;
  }

  return valueRef.current!;
}

关键点:

  • 它使用 useRef手动管理缓存,完全绕过了 React 对 useMemo 的潜在“丢弃”行为。
  • 只要依赖项没变,就不会重新执行 factory 函数,100% 保证创建一次。

因此,当你需要“确定性地只创建一次对象”时(比如 new 实例、复杂计算、带副作用的初始化),应该用 useCreation 而不是 useMemo


useRef 呢?

useRef 也可以用来持久化值,但它不具备依赖更新的能力。例如:

const obj = useRef(new ExpensiveObject()).current;

这个 obj 永远不会更新,即使你希望在某些 props 变化时重新创建它,useRef 无法做到。

useCreation 在依赖变化时会重新创建,不变化时保持不变——兼具 useMemo 的依赖响应性和 useRef 的稳定性


总结

| Hook | 是否保证不重复计算 | 支持依赖更新 | 适合场景 |
|--------------|------------------|------------|--------|
| useMemo | ❌(React 可能丢弃) | ✅ | 纯计算、性能优化 |
| useRef | ✅(但永不更新) | ❌ | 持久化可变值 |
| useCreation| ✅(严格只创建一次) | ✅ | 创建昂贵/有副作用的对象(推荐替代 useMemo 用于实例化) |

所以 ahooks 推荐:

“创建对象用 useCreation,计算值用 useMemo。”

这也是为什么你看到文档说:useCreationuseMemouseRef 的替代品——它在“创建对象”这个特定场景下,比两者都更安全、更合适。