useState Hook 分析

82 阅读3分钟

useState() 函数是 React 中的一个 Hook,用于在函数式组件中引入状态。该函数的完整实现涉及到一些 React 内部的机制,下面我们来一步步解读。

首先,useState() 函数的实现是基于 useStateImpl 函数的。具体来说,useState() 函数会调用 useStateImpl 函数,并且将组件的状态初始化值作为参数传递给它:

function useState(initialState) {
  // 调用 useStateImpl 函数,并且将 initialState 作为参数传递给它
  return useStateImpl(initialState);
}

然后,我们来看一下 useStateImpl 函数的完整实现。下面是 useStateImpl 函数的源码,带有注释解释每一步的细节:

// React 内部维护的变量,表示当前正在渲染的组件
let currentlyRenderingComponent;
// React 内部维护的变量,表示当前正在渲染的 hook 的索引
let currentHookIndex = 0;

function useStateImpl(initialState) {
  // 获取当前正在渲染的组件
  const component = currentlyRenderingComponent;
  // 获取当前正在渲染的 hook 的索引
  const index = currentHookIndex;

  // 如果是初次渲染,则从组件的 memoizedState 中获取该 hook 的 state
  if (component.memoizedState === null) {
    // 创建一个数组用于存储所有的 hooks
    component.memoizedState = [initialState, function setState(newState) {
      // 更新 state
      component.memoizedState[index][0] = newState;
      // 触发重新渲染
      component.update();
    }];
  }

  // 获取该 hook 的 state 和 setState 函数
  const [state, setState] = component.memoizedState[index];

  // 更新当前正在渲染的 hook 的索引
  currentHookIndex++;

  // 返回 state 和 setState 函数
  return [state, setState];
}

对上面代码的解释:

  1. React 内部维护了两个全局变量,分别是 currentlyRenderingComponentcurrentHookIndex。其中 currentlyRenderingComponent 表示当前正在渲染的组件,currentHookIndex 表示当前正在渲染的 hook 在该组件中的索引。

  2. useStateImpl 函数首先获取当前正在渲染的组件和正在渲染的 hook 的索引。这里需要注意的是,组件对象中有一个名为 memoizedState 的属性,它用于存储该组件中所有的 hook 的状态和 setState 函数。如果是初次渲染,memoizedState 的值为 null,我们需要为它创建一个数组,数组的第一个元素是状态的初始值,第二个元素是 setState 函数。

  3. setState 函数是一个闭包函数,它能够访问到当前正在渲染的组件对象、当前正在渲染的 hook 的索引和状态的初始值。在 setState 函数中,我们更新了 hook 的状态,并且触发重新渲染。

  4. 接下来,我们获取该 hook 的状态和 setState 函数,并且更新当前正在渲染的 hook 的索引。最后,返回状态和 setState 函数的数组。

总的来说,useState() 函数的实现原理是基于 React 内部维护的 memoizedState 属性来实现的。当组件初次渲染时,memoizedState 的值为 null,我们需要为它创建一个数组,数组的第一个元素是状态的初始值,第二个元素是 setState 函数。在组件重新渲染时,我们可以直接从 memoizedState 中获取该 hook 的状态和 setState 函数,无需重新创建。这种实现方式可以保证 hook 的状态和 setState 函数能够正确地关联到组件上,同时也能够避免状态和函数被重复创建。