Article
从零实现 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-constlet stateHooks = []// eslint-disable-next-line prefer-constlet 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
// ...更新}