React源码学习---useState

56 阅读2分钟

useState的实现使用了一个updateQueue队列来缓存等待更新的状态和更新操作,用到了一些异步更新和批量更新的技巧,以提高组件的性能。

总结为以下几个步骤:
  1. 在useState函数中,定义了一个名为currentlyRenderingComponent的变量,用于存放当前正在渲染的组件信息。
  2. 在useState函数中,首先判断当前是否正在进行批量更新,如果是,直接使用上次的更新值;否则,就根据参数initialState生成新的状态值和状态更新函数并返回。
  3. 定义了一个createDispatcher函数,用于创建新的状态更新函数。它首先定义了一个value变量存放当前状态的值,并根据传入的新状态值来更新它,然后调用scheduleRender函数告知React需要进行重新渲染。
  4. 定义了scheduleRender函数,用于触发组件的重新渲染。如果当前正在批量更新状态,则不会立即进行更新,而是先将所有更新操作加入pending队列中,等待所有的更新操作处理完毕后,一起进行更新。
  5. 定义了一个名为setState的更新函数,它接收两个参数,第一个参数为待更新的状态的索引值,第二个参数为新的状态值。在setState函数中,首先更新pending队列中对应的元素值,然后调用scheduleRender函数触发组件的重新渲染。
// 源码
let currentlyRenderingComponent;
function useState(initialState) {
  const component = currentlyRenderingComponent || { 
    updaterQueue: {
      pending: [], // 更新队列,用于存放所有等待更新的 setter 
      batching: false // 指示是否处于批量更新模式 
    } 
  };
  const {updaterQueue} = component; // 获取更新队列

  // 如果正在处于批量更新模式,则使用上一次的值,否则使用传入的初始值
  const currentIndex = updaterQueue.pending.length;
  if (currentIndex) {
    return [updaterQueue.pending[currentIndex - 1].value, setState.bind(null, currentIndex)];
  } else {
    const [val, dispatch] = createDispatcher(initialState);
    return [val, dispatch];
  }

  function setState(index, newState) {
    // 使用updaterQueue更新pending队列中的值
    updaterQueue.pending[index].value = newState;
    try {
      scheduleRender(component);
    } catch (err) {
      console.log(err);
    }
  }
}

// 创建状态更新函数
function createDispatcher(initialState) {
  let value = initialState;
  const dispatch = newState => {
    value = typeof newState === "function" ? newState(value) : newState;
    try {
      const component = currentlyRenderingComponent;
      scheduleRender(component); // 触发重新渲染 
    } catch (err) {
      console.log(err);
    }
  };
  return [value, dispatch];
}

// 触发重新渲染
function scheduleRender(component) {
  // 如果正在进行批量更新,则不会立即更新,等待队列中所有的setter处理完后,统一进行更新
  if (component.updaterQueue.batching) return;
  component.updaterQueue.batching = true;

  Promise.resolve().then(() => {
    let { updaterQueue } = component;
    if (!updaterQueue.pending.length) {
      component.updaterQueue.batching = false;
      return;
    }

    // 批量更新状态
    let i = 0;
    while (i < updaterQueue.pending.length) {
      const current = updaterQueue.pending[i];
      current.execute();
      i++;
    }

    // 清空pending队列,继续进行下一次更新
    updaterQueue.pending = [];
    component.updaterQueue.batching = false;
  });
}