react hooks原理,你真的了解useState吗?

useState是一个ReactHook,用于在函数组件中管理内部状态。它返回当前状态和一个更新状态的函数。状态更新可能是异步的,且React通过Object.is算法比较新旧状态以决定是否实际渲染。文章还讨论了函数式更新、惰性初始状态、性能优化策略如useCallback和useMemo,以及useState在挂载和更新阶段的内部实现细节。
摘要由CSDN通过智能技术生成

useState

  • useState 就是一个 Hook
  • 通过在函数组件里调用它来给组件添加一些内部 state,React 会在重复渲染时保留这个 state
  • useState 会返回一对值:当前状态和一个让你更新它的函数,你可以在事件处理函数中或其他一些地方调用这个函数。它类似 class 组件的 this.setState,但是它不会把新的 state 和旧的 state 进行合并
  • useState 唯一的参数就是初始 state
  • 返回一个 state,以及更新 state 的函数
  • 在初始渲染期间,返回的状态 (state) 与传入的第一个参数 (initialState) 值相同
    setState 函数用于更新 state。它接收一个新的 state 值并将组件的一次重新渲染加入队列
每次渲染都是独立的闭包
  • 每一次渲染都有它自己的 Props and State
  • 每一次渲染都有它自己的事件处理函数
  • 捕获渲染值特性 alert会“捕获”我点击按钮时候的状态
  • 在单次渲染的范围内,props和state始终保持不变
函数式更新
  • 如果新的 state 需要通过使用先前的 state 计算得出,那么可以将函数传递给 setState。该函数将接收先前的 state,并返回一个更新后的值
惰性初始 state
  • initialState 参数只会在组件的初始渲染中起作用,后续渲染时会被忽略
  • 如果初始 state 需要通过复杂计算获得,则可以传入一个函数,在函数中计算并返回初始的 state,此函数只在初始渲染时被调用
性能优化
  • 调用 State Hook 的更新函数并传入当前的 state 时,React 将跳过子组件的渲染及 effect 的执行。(React 使用 Object.is 比较算法 来比较 state)
  • 减少渲染次数
    • 把内联回调函数及依赖项数组作为参数传入 useCallback,它将返回该回调函数的 memoized 版本,该回调函数仅在某个依赖项改变时才会更新
    • 把创建函数和依赖项数组作为参数传入 useMemo,它仅会在某个依赖项改变时才重新计算 memoized 值。这种优化有助于避免在每次渲染时都进行高开销的计算
useState深入学习
  • useState是内置执行器的useReducer
  • useState相对于useReducer增加了一些优化比如eagerState(急切的状态)
挂载阶段:
// 当前函数组件的fiber树
let currentlyRenderingFiber = null;
// 当前hook对象的fiber树
let workInProgressHook = null;
// 首次挂在
function mountState(initialState) {
  const hook = mountWorkInProgressHook(); // 获取当前hook对应的fiber
  hook.memoizedState =  hook.baseState = initialState; // 首次挂载时初始化状态赋值
  const queue = { 
    pending: null, 
    dispatch: null,
    lastRenderedReducer: basicStateReducer,
    lastRenderedState: initialState,
 };
  hook.queue = queue; // 更新队列赋值
  const dispatch = (queue.dispatch = dispatchSetState.bind(null, currentlyRenderingFiber, queue));
  return [hook.memoizedState, dispatch];
}
function mountWorkInProgressHook() {
  const hook = { 
    memoizedState: null, // hook的状态
    queue: null, // 存放着一个单向循环链表,存放所有的更新
    next: null, // 指针 指向下一个hook
  };
  // currentlyRenderingFiber.memoizedState存放着当前函数组件中所有hook的单链表
  // workInProgressHook存放单链表中的最后一个hook
  if (workInProgressHook === null) {
    currentlyRenderingFiber.memoizedState = workInProgressHook = hook; 
  } else {
    workInProgressHook = workInProgressHook.next = hook;
  }
  return workInProgressHook;
}
function dispatchSetState(fiber, queue, action) {
  const update = { // 每一个更新
    action, 
    hasEagerState: false, // 开关标识
    eagerState: null, // 急切的状态
    next: null, // 指针 指向下一个更新
  }; // 可以把每次useState调用看作一次更新
  const lastRenderedReducer = queue.lastRenderedReducer; // 缓存上一次计算函数
  const currentState = queue.lastRenderedState; // 缓存上一次渲染状态
  const eagerState = lastRenderedReducer(currentState, action); // 在开启在根结点上调度更新之前 先进行一次计算
  update.hasEagerState = true; 
  update.eagerState = eagerState;
  if (is(eagerState, currentState)) { // 如果计算结果较上次没有变化 则直接跳过之后的调度阶段(useState独有的优化)
    return;
  }
  const root = enqueueConcurrentHookUpdate(fiber, queue, update); // 入队更新
  scheduleUpdateOnFiber(root, fiber); // 从根节点开启调度更新
}
更新阶段(复用useReducer的更新方法)
let currentHook = null; // 当前hook
function updateReducer(reducer) {
  const hook = updateWorkInProgressHook() // 获取更新后的hook
  const queue = hook.queue // 缓存更新队列
  queue.lastRenderedReducer = reducer // 更新缓存函数
  const current = currentHook // 缓存当前hook
  const pendingQueue = queue.pending // 获取到hook上的更新队列
  let newState = current.memoizedState // 获取hook上的状态
  if (pendingQueue !== null) { 
    queue.pending = null // 剪断循环链表
    const first = pendingQueue.next
    let update = first // 缓存第一个更新
    do {
      if (update.hasEagerState) { // 如果存在急切的更新状态 则不要重新计算新状态了
        newState = update.eagerState
      } else {
        const action = update.action
        newState = reducer(newState, action)
      }
      update = update.next // 不断递归
    } while (update !== null && update !== first) // 截止条件 
  }
  hook.memoizedState = queue.lastRenderedState = newState 更新hook状态
  return [hook.memoizedState, queue.dispatch]
},

function updateWorkInProgressHook() {
  if (currentHook === null) { 
    const current = currentlyRenderingFiber.alternate // 获取当前函数式组件的轮替
    currentHook = current.memoizedState // 获取当前函数组件的hook单链表
  } else {
    currentHook = currentHook.next 
  }
  const newHook = { // 新的hook
    memoizedState: currentHook.memoizedState, // 状态
    queue: currentHook.queue, // 更新队列
    next: null // 指向下一个hook
  }
  // currentlyRenderingFiber.memoizedState 存放着当前函数组件中所有的hook 是一个单链表
  // workInProgressHook是hook单链表中最后一个hook
  if (workInProgressHook === null) {
    currentlyRenderingFiber.memoizedState = workInProgressHook = newHook
  } else {
    workInProgressHook = workInProgressHook.next = newHook
  }
  return workInProgressHook // 返回当前的hook
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
React Hooks 是 React 16.8 版本引入的一种新特性,它是为了使函数组件能够拥有状态和其他 React 特性而设计的。 React Hooks 的原理基于两个核心概念:闭包和钩子函数。 1. 闭包:在函数组件内部,可以通过闭包的方式引用外部作用域的变量。React Hooks 利用了闭包的特性,使得可以在函数组件内部存储和更新状态。 2. 钩子函数:React Hooks 提供了一系列的钩子函数,如 useStateuseEffect、useContext 等。这些钩子函数是预定义的特殊函数,可以在函数组件中使用,通过调用这些钩子函数,可以获取和操作组件的状态、副作用和上下文等。 当一个组件使用了 React Hooks,React 在底层会创建一个与该组件实例相关联的 Fiber 对象,并在组件渲染时执行组件函数。在执行组件函数时,React 跟踪每个组件函数内部所有的钩子函数调用,并将其与该组件实例相关联。 当组件函数执行时,如果遇到 useState 钩子调用,React 会查找该钩子函数对应的状态值,并将其返回给组件函数。组件通过 useState 返回的状态值可以读取和更新组件的状态。 当组件函数执行完毕后,React 会将该组件的状态和副作用存储在 Fiber 对象中,并将 Fiber 对象添加到更新队列中。之后,React 会根据更新队列中的 Fiber 对象,对组件进行批量更新,实现页面的重新渲染。 通过这种方式,React Hooks 实现了函数组件的状态管理和副作用处理,使得开发者可以更方便地编写和维护 React 组件。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值