useCreation 与 useMemo
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。”
这也是为什么你看到文档说:useCreation 是 useMemo 或 useRef 的替代品——它在“创建对象”这个特定场景下,比两者都更安全、更合适。