1

先要知道几点前提要点

  1. 在functional component中,每一次props的变化、执行setState操作都会导致组件方法重新执行。
  2. 基于1,组件方法执行后,所有的直接定义的const、let变量都会重新定义。
  3. 所以对于不需要变化的常量,一般用useRef封装起来。

常见的使用方法:
const a = useRef(initalValue);
在执行组件方法的时候,a不会被重新赋值。那么useRef在这里本质上干了什么呢?

useRef本质上是ReactCurrentDispatcher.current上的一个方法,这个方法接收一个初始值,返回一个对象,这个对象中只有一个current属性。

在执行组件方法的时候,这个current的值不会被重置,也就是说,每一次执行组件方法,用到的都是同一个值。

当然这里有一个问题,返回的这个对象为什么不会被重置?

应该是react用了一种时序的保存方法,在初始化时,将值记录在一个对象中;重复执行组件方法的时候,再从这个对象中取出来赋值到了a上。

下面看源码片段,这是初始化ref的源码:

useRef: function (initialValue) {
  currentHookNameInDev = 'useRef';
  mountHookTypesDev();
  return mountRef(initialValue);
},
function mountRef(initialValue) {
  var hook = mountWorkInProgressHook();
  var ref = {
    current: initialValue
  };

  {
    Object.seal(ref);
  }

  hook.memoizedState = ref;
  return ref;
}

附:Object.seal只是让ref里的key无法删除,value可以改变。

下面是更新ref(第二次以后执行组件方法)的代码片段:

useRef: function (initialValue) {
  currentHookNameInDev = 'useRef';
  updateHookTypesDev();
  return updateRef(); // 可以注意到这里是update
},

这里mountHookTypesDev和updateHookTypesDev不重要。主要逻辑是mountRef和updateRef方法。
可以看到,updateRef其实就是返回了hook里记录的memoizedState
至于mountWorkInProgressHook和updateWorkInProgressHook,是两个比较复杂的方法。涉及到hook的本质,实际上useState也是用这个方式存储值的。

详见另一篇文章:react hooks源码核心:workInProgressHook函数


aaron_lawliet
178 声望2 粉丝