useState

103 阅读2分钟

useState

使用

  • 更新状态并刷新,可以传递函数或值
  • 与reducer有一定区别,暂时可以理解为useReducer语法糖
import { useReducer, useState } from "react";
const App = () => {
  const [num, setNum] = useReducer(controll, 0);
  const [state, setState] = useState(0);
  console.log("会更新");
  return (
    <div>
      <p>{state}</p>
      <p>{num}</p>
      <button
        onClick={() => {
          setState(0);
        }}
      >
        state按钮
      </button>
      <button
        onClick={() => {
          setNum(0);
        }}
      >
        reducer按钮
      </button>
    </div>
  );
};

源码

  • 与useReducer一样,都是在函数执行前给react的变量添加实例,然后通过变量.current.useState调用
...
export function useState(reducer, initialArg) {
  const dispatcher = resolveDispatcher();
  return dispatcher.useState(reducer, initialArg);
}
const HooksDispatcherOnMount = {
  useReducer: mountReducer,
+ useState: mountState
}
const HooksDispatcherOnUpdate = {
  useReducer: updateReducer,
+ useState: updateState
}

// 首次挂载
function mountState(initialState) {
  const hook = mountWorkInProgressHook();
  hook.memoizedState = initialState;
  const queue = {
    pending: null,
    dispatch: null,
    lastRenderedReducer: baseStateReducer,//上一个reducer
    lastRenderedState: initialState//上一个state
  }
  hook.queue = queue;
  const dispatch = (queue.dispatch = dispatchSetState.bind(null, currentlyRenderingFiber, queue));
  return [hook.memoizedState, dispatch];
}
  • state的创建hook实例与链表关联都与useReducer一样

与useReducer的差异

  • 在hook上可以看到多出了lastRenderedReducerlastRenderedState这两个属性

lastRenderedReducer

  • lastRenderedReducer对应的是一个函数,也可以叫做内置的reducer
    • 我们在更新状态的时候,可以传函数,也可以传递值
    • 这个函数的作用就是区分我们传递进来的类型,然后返回最终结果
function baseStateReducer(state, action) {
  return typeof action === 'function' ? action(state) : action;
}

lastRenderedState

  • 这个就是上一次的状态

dispatch

  • dispatch的形式也没有变化,但是并没有复用useReducerdispatchReducerAction,而是调用了一个新的函数dispatchSetState
  • 因为useState有一个性能优化点,当新旧两个值不同的时候才进行视图的刷新
  • 而在学习useReducer的时候是先调用dispatch将更新入队,然后渲染视图,视图重新执行到hook的时候,会将队列内容取出更新,然后渲染,但这样得等到最后才拿返回结果,与useState的特性不符
  • dispatchSetState执行步骤:
    • 将旧值与传入的action(函数或值)通过lastRenderedReducer执行,拿到本次更新结果
    • 然后拿到新旧两个值进行比较
    • 相同,就不入队列,也不刷新视图了,直接return
    • 反之则入队列,刷新视图
// 入队并更新
// 注意:有前置判断
function dispatchSetState(fiber, queue, action) {
  const update = {
    action,
    hasEagerState: false,//是否有急切的更新
    eagerState: null,//急切的更新状态
    next: null
  }
  //当你派发动作后,我立刻用上一次的状态和上一次的reducer计算新状态
  const { lastRenderedReducer, lastRenderedState } = queue;
  const eagerState = lastRenderedReducer(lastRenderedState, action);
  update.hasEagerState = true;
  update.eagerState = eagerState;
  if (Object.is(eagerState, lastRenderedState)) {
    return;
  }
  //下面是真正的入队更新,并调度更新逻辑
  const root = enqueueConcurrentHookUpdate(fiber, queue, update);
  scheduleUpdateOnFiber(root);
}

更新

  • 直接把baseStateReducer传给updateReducer了,因为updateReducer得接收个函数,可以理解为完全复用useReducer更新逻辑
function updateState() {
  return updateReducer(baseStateReducer);
}