useState

63 阅读2分钟

v1.0

type Hook = {
  oldState: any
  state: any
  queue: Function[]
}
let hook: Hook = null

function useState_(initState: any) {
  /**
   * 由于render函数的代码量太大,这里就简单的代替一下
   * 真实的render函数干了些什么
   * 1, 重新构建fiber树
   * 2, 构建完fiber树后对真实的dom进行增删改
   */
  const [_, render] = useState({})
  /**
   * useState第一次执行时走这里
   * 也就是组件加载的时候
   */
  if (!hook) {
    hook = {
      state: initState,
      queue: [],
      oldState: null,
    }
  }
  /**
   * 组件渲染时, 执行setState存放的回调函数,得到新的hook.state
   * useState第一次执行时,也就是组件加载时,hook.queue为空,什么也不做
   */
  hook.queue.forEach(action => {
    hook.state = action(hook.oldState)
  })
  /**
   * setState的回调函数什么时候执行:
   * setState(callball), setState执行时callback会被存起来
   * 组件重新渲染时, callback会执行,修改hoo.state值,得到新的state
   */
  const setState = (action: any) => {
    hook.oldState = hook.state
    hook.queue.push(action)
    render({})
  }

  return [hook.state, setState] as const
}

v2.0

let wipFiber = null;// 正在构建的fiber节点
let hookIndex = null;

function updateFunctionComponent(fiber) {
  wipFiber = fiber; // 保存下当前正在处理的fiber,这样做的目的是可以任务中断再开始后可以快速恢复之前的状态
  hookIndex = 0;    // useState每执行一次, hookIndex+1
  wipFiber.hooks = []; // useState每执行一次, wipFiber.hooks.push(hook)
  const children = [fiber.type(fiber.props)]; // 如果fiber是函数组件,那就执行type方法,把props传过去,返回值又是一个jsx转createElement
  reconcileChildren(fiber, children); // 继续调用协调器来处理父fiber和子fiber之间的关系
}

/**
 * 函数是组件每一次都会调用这个hook,然后遍历actions有没有
 * 有就一次执行action然后将action的结果赋值给hook的state
 * 最后由
 */
function useState(initial) { // 初始值就是函数式组件里面定义的useState,这个也会重定向到这个函数
  const oldHook =
    wipFiber.alternate &&
    wipFiber.alternate.hooks &&
    wipFiber.alternate.hooks[hookIndex];
  /**
   * hook.state 的值由 oldHook 决定
   * 
   * 组件更新因别人而起:
   *   oldHook.queue = [], 
   *   hook.state === oldHook.state, 
   *   状态保持不变
   *   setState未执行
   *    
   * 组件更新因自己而起:
   *    oldHook.queue不为空, 
   *    hook.state的值由oldHook.queue决定, 
   *    状态将会发生变化
   *    setState被执行
   */
  const hook = { 
    state: oldHook ? oldHook.state : initial, // wipFiber的最新值, wipFiber.alternate 存放着 wipFiber 需要的最新值
    queue: [],// 用于存放setState的回调函数, 这里存放着正在构建的fiber节点的最新值
  };

  const actions = oldHook ? oldHook.queue : [];
  // action 是 setState 的回调函数
  // wipFiber.alternate 存放着 wipFiber 需要的最新值
  actions.forEach((action) => {
    hook.state = action(hook.state);//????????
  });

  const setState = (action) => { 
    hook.queue.push(action); // 把所有的改变都放到队列里
    wipRoot = {
      dom: currentRoot.dom,
      props: currentRoot.props,
      /**
       * 在每一个 fiber 节点上添加 alternate 属性用于记录旧 fiber 节点
       *(上一个 commit 阶段使用的 fiber 节点)的引用。
       */
      alternate: currentRoot,
    };
    nextUnitOfWork = wipRoot;
    deletions = [];
  };

  wipFiber.hooks.push(hook);
  hookIndex++;
  return [hook.state, setState]; // 把state和setState函数暴露出去
}