文章目录
从零实现 mini-react(5):实现useState
1.实现useState的实现,多个state需要用数组存起来,配合对应index,依次配合action执行才能正确获取值。
useState 的实现
首先拿到当前的 fiber,保存起来
内部初始化一个 stateHook 对象,他的 value 保存对应值 (即初始值)
function useState(initial) {
  const currentFiber = wipFiber
  const stateHook = {
    value: initial
  }
  function setState(action) {
    // todo:
  }
  return [stateHook.value, setState]
}
然后 setState 内部取更新 value,触发更新:
function setState(action) {
  stateHook.value = action(stateHook.value)
  wipFiber = {
    ...currentFiber,
    alternate: currrentFiber
  }
  requestIdleCallback(loop)
}
然后触发更新后,会重新调用到该 useState,但是这时候 value 还是 initial 就不对了,应该是上一次运行时创建的 stateHook 的 value
因此我们需要把每次 useState 中创建的 stateHook 存起来,我们可以做个全局变量
// eslint-disable-next-line prefer-const
let stateHooks = []
// eslint-disable-next-line prefer-const
let stateHookIndex = 0
因为有多个 useState,所以用一个数组来存,然后按序号取,这也是为什么 useState 只能再函数顶部
然后我们在更新函数组件时,重置他们
function updateFunctionComponent(fiber) {
  // ...
  stateHooks = []
  stateHookIndex = 0
}
然后在 useState 内去存储他们:
function useState(initial) {
  const currentFiber = wipFiber
  // 3 尝试拿到 1 存起来的 stateHook
  const oldStateHook = currentFiber?.stateHooks?.[stateHookIndex]
  const stateHook = {
    // 4. 拿到 2 更新的值
    value: oldStateHook ? oldStatHook.value : initial
  }
  // 按执行顺序,放到数组里
  stateHooks.push(stateHook)
  // 1. 存好
  currentFiber.stateHooks = stateHooks
  function setState(action) {
    // 2. 更新好值
    stateHook.value = action(stateHook.value)
    // ... 开始更新
  }
}
批量 action 处理
我们没必要在 setState 内就做 action 的调用,可以在 useState 调用时,再将之前存起来的 action 全调用完
function useState(initial) {
  const currentFiber = wipFiber
  // 3 尝试拿到 1 存起来的 stateHook
  const oldStateHook = currentFiber?.stateHooks?.[stateHookIndex]
  const stateHook = {
    // 4. 拿到 2 更新的值
    value: oldStateHook ? oldStatHook.value : initial
   queue: oldStateHook ? oldStatHook.queue : []
  }
  stateHook.queue.forEach((action) => {
   // 更新,方便下一次更新的时候,从 alternate 中的stateHooks获取到
   stateHook.value = action(stateHook.value)
  })
 // 清空
 stateHook.queue = []
  // 按执行顺序,放到数组里
  stateHooks.push(stateHook)
  // 1. 存好
  currentFiber.stateHooks = stateHooks
  function setState(action) {
    // 2. 更新好值
   stateHook.queue.push(action)
    // ... 开始更新
  }
}
当前这里的 action 只支持函数,我们可以做个判断,支持值得传入
const normalAction = typeof action === 'function' ? action : () => action
优化更新
如果说更新后得值和原来的值不变,则没必要去更新了,因此我们可以先执行下 action
function setState(action) {
  const eager = action(stateHook.value)
  if (eager === stateHook)
    return
  // ...更新
}