起因
这两个 API 可能不够 stable,未来可能导致代码库里用到的地方需要进行相关改动
具体可以看官方文档中有这么一段话
You may rely on useMemo as a performance optimization, not as a semantic guarantee. In the future, React may choose to “forget” some previously memoized values and recalculate them on next render, e.g. to free memory for offscreen components. Write your code so that it still works without useMemo — and then add it to optimize performance.
总结一下 React core team 成员对这个问题的回复
如果未来真的上这个 feature
- 会提供额外的 API 来 mark 当前组件是否允许进行内存回收
- 只会在「能够合理破坏 memoization」的地方进行回收,比如「一个 tab 隐藏了几分钟,可以释放掉仅仅被 memoization 占用的额外内存」
社区已经有的替代方案
useMemoOne
基本是用 useRef
重新实现了一遍 useMemo
和 useCallback
useCreation
ahooks 的一个 useMemo
的替代品 或者说是根据 deps 变化的 useRef
目前 useRef
和 useMemo
useCallback
具体实现原理
function useRef<T>(initialValue: T): {|current: T|} {
currentlyRenderingComponent = resolveCurrentlyRenderingComponent();
workInProgressHook = createWorkInProgressHook();
const previousRef = workInProgressHook.memoizedState;
if (previousRef === null) {
const ref = {current: initialValue};
if (__DEV__) {
Object.seal(ref);
}
workInProgressHook.memoizedState = ref;
return ref;
} else {
return previousRef;
}
}
function useMemo<T>(nextCreate: () => T, deps: Array<mixed> | void | null): T {
currentlyRenderingComponent = resolveCurrentlyRenderingComponent();
workInProgressHook = createWorkInProgressHook();
const nextDeps = deps === undefined ? null : deps;
if (workInProgressHook !== null) {
const prevState = workInProgressHook.memoizedState;
if (prevState !== null) {
if (nextDeps !== null) {
const prevDeps = prevState[1];
if (areHookInputsEqual(nextDeps, prevDeps)) {
return prevState[0];
}
}
}
}
if (__DEV__) {
isInHookUserCodeInDev = true;
}
const nextValue = nextCreate();
if (__DEV__) {
isInHookUserCodeInDev = false;
}
workInProgressHook.memoizedState = [nextValue, nextDeps];
return nextValue;
}
export function useCallback<T>(
callback: T,
deps: Array<mixed> | void | null,
): T {
return useMemo(() => callback, deps);
}
可见 useCallback
基本可以认为是为了传入函数更方便(少写一层 () => fn
)的 useMemo
,因此也会被这个问题牵扯
相关资料
- 关于这个问题的详细 issue 讨论: https://github.com/facebook/react/issues/15278
- 比较流行的一个 useMemo 使用 useRef 重写的内存安全的替代品 useMemoOne: https://github.com/alexreardon/use-memo-one
- React core team 成员在 Reddit 上的回复: https://www.reddit.com/r/reactjs/comments/mib8mr/usememo_docs_dont_reflect_how_it_is_used_in/gt6ouh8?utm_source=share&utm_medium=web2x&context=3
- 各种 hooks 的具体实现: https://github.com/facebook/react/blob/cae635054e17a6f107a39d328649137b83f25972/packages/react-dom/src/server/ReactPartialRendererHooks.js
PS MST observer 用的是 React 的 top-level API React.memo 不是 useMemo
,所以暂时不用担心
Top comments (0)