写点什么

createRef、useRef、useMemo 对比分析和应用场景

用户头像
费马
关注
发布于: 2020 年 06 月 05 日
createRef、useRef、useMemo对比分析和应用场景

useRef vs createRef

在hook出现之前,通过使用createRef来获取dom节点实例,调用createRef会返回一个新的ref,在class组件中我们可以在构造函数中创建一个ref并赋值给组件属性:

class App extends React.Component {
contructor() {
this.inputRef = createRef();
}
}



但在hook中使用时,每次渲染时都会重新创建一个ref,导致组件重新渲染,因此useRef出来后可以解决该问题,保证每次返回的内容都是初始化的值。



createRef:

// an immutable object with a single mutable value
export function createRef(): RefObject {
const refObject = {
current: null,
};
if (__DEV__) {
Object.seal(refObject);
}
return refObject;
}

可见每次调用createRef时,返回的都是一个新的object,且该对象不可修改,但其内部属性current是可以动态修改的。



看下useRef的实现:

function mountRef<T>(initialValue: T): {|current: T|} {
const hook = mountWorkInProgressHook();
const ref = {current: initialValue};
if (__DEV__) {
Object.seal(ref);
}
hook.memoizedState = ref;
return ref;
}
function updateRef<T>(initialValue: T): {|current: T|} {
const hook = updateWorkInProgressHook();
return hook.memoizedState;
}



useRef vs useMemo



在React仓库的issue中,有人提出了用useMemo来模拟实现useRef,栗子如下:

const ref = useRef(null);
const ref2 = useMemo(() => { current: null }, []);



ref2使用useMemo来缓存一个对象:{current: null},并且不指定依赖项,想实现只更新一次的目的。但实际上效率还是不如useRef,有人贴出了源码来对比:



function mountRef<T>(initialValue: T): {|current: T|} {
const hook = mountWorkInProgressHook();
const ref = {current: initialValue};
if (__DEV__) {
Object.seal(ref);
}
hook.memoizedState = ref;
return ref;
}
function updateRef<T>(initialValue: T): {|current: T|} {
const hook = updateWorkInProgressHook();
return hook.memoizedState;
}



可以看出,每次更新ref时,返回的都是第一次缓存的对象,因此不会触发重复渲染。

useMemo:

function mountMemo<T>(
nextCreate: () => T,
deps: Array<mixed> | void | null,
): T {
const hook = mountWorkInProgressHook();
const nextDeps = deps === undefined ? null : deps;
const nextValue = nextCreate();
hook.memoizedState = [nextValue, nextDeps];
return nextValue;
}
function updateMemo<T>(
nextCreate: () => T,
deps: Array<mixed> | void | null,
): T {
const hook = updateWorkInProgressHook();
const nextDeps = deps === undefined ? null : deps;
const prevState = hook.memoizedState;
if (prevState !== null) {
// Assume these are defined. If they’re not, areHookInputsEqual will warn.
if (nextDeps !== null) {
const prevDeps: Array<mixed> | null = prevState[1];
if (areHookInputsEqual(nextDeps, prevDeps)) {
return prevState[0];
}
}
}
const nextValue = nextCreate();
hook.memoizedState = [nextValue, nextDeps];
return nextValue;
}



可见useMemo是会追踪和对比依赖项,而且一定会执行。useMemo使用语法如下:

const memoizedValue = useMemo(() => computeExpensiveValue(a, b), [a, b]);



因此useRef通常用来保存数据,而useMemo来保存会根据依赖变化而变化的数据,当依赖数据未发送变化时useMemo返回值总是一样的,可以避免重复渲染,提高组件性能。当然,你也可以使用useMemo来缓存组件:

by Dan Abramov

使用建议:不要上来就使用useMemo,这样可以避免过度优化,你应该在需要优化的时候再考虑这些优化手段。

传送门

react/ReactCreateRef.js

react/ReactFiberHooks.js



发布于: 2020 年 06 月 05 日阅读数: 297
用户头像

费马

关注

一个想当厨子的程序员@杭州 2018.07.28 加入

公众号:【也寻常】 在杭州的同学,来加个微信吧

评论 (1 条评论)

发布
用户头像
对文章内容有问题的朋友,欢迎评论与交流
2020 年 06 月 05 日 14:39
回复
没有更多了
createRef、useRef、useMemo对比分析和应用场景