useState原理解析_51CTO博客_usestate原理


本站和网页 https://blog.51cto.com/feng/5289731 的作者无关,不对其内容负责。快照谨为网络故障时之索引,不代表被搜索网站的即时页面。

useState原理解析_51CTO博客_usestate原理
useState原理解析
关注
前端早茶
文章目录
一、初始化
二、更新时
三、 了解useState
useState的引入
useState的实现
四. 核心步骤分析
Hook对象的结构如下:
renderWithHooks
总结
useState原理解析
原创
前端早茶
2022-05-12 20:30:29
©著作权
文章标签
初始化
链表
触发器
文章分类
JavaScript
前端开发
©著作权归作者所有:来自51CTO博客作者前端早茶的原创作品,请联系作者获取转载授权,否则将追究法律责任
一、初始化构建dispatcher函数和初始值二、更新时调用dispatcher函数,按序插入update(其实就是一个action)收集update,调度一次React的更新在更新的过程中将​​ReactCurrentDispatcher.current​​指向负责更新的Dispatcher执行到函数组件App()时,​​useState​​会被重新执行,在resolve dispatcher的阶段拿到了负责更新的dispatcher。​​useState​​会拿到Hook对象,​​Hook.query​​中存储了更新队列,依次进行更新后,即可拿到最新的state函数组件App()执行后返回的nextChild中的count值已经是最新的了。FiberNode中的​​memorizedState​​也被设置为最新的stateFiber渲染出真实DOM。更新结束三、 了解useStateuseState的引入// React.jsimport {useCallback,useContext,useEffect,useImperativeHandle,useDebugValue,useLayoutEffect,useMemo,useReducer,useRef,useState,} from './ReactHooks';所有的Hooks在​​React.js​​中被引入,挂载在React对象中useState的实现// ReactHooks.jsexport function useState<S>(initialState: (() => S) | S) { const dispatcher = resolveDispatcher(); return dispatcher.useState(initialState);}重点都在这个​​dispatcher​​​上,​​dispatcher​​​通过​​resolveDispatcher()​​​来获取,这个函数同样也很简单,只是将​​ReactCurrentDispatcher.current​​​的值赋给了​​dispatcher。​​// ReactHooks.jsfunction resolveDispatcher() { const dispatcher = ReactCurrentDispatcher.current; return dispatcher;}​​ReactCurrentDispatcher.current.useState​​​是​​useState​​能够触发更新的关键原因,这个方法的实现并不在react包内。四. 核心步骤分析ReactFiberHooks.js包含着各种关于Hooks逻辑的处理Hook对象的结构如下:// ReactFiberHooks.jsexport type Hook = { memoizedState: any, baseState: any, baseUpdate: Update<any, any> | null, queue: UpdateQueue<any, any> | null, next: Hook | null, };在类组件中​​state​​​是一整个对象,可以和​​memoizedState​​​一一对应。但是在​​Hooks​​​中,React并不知道我们调用了几次​​useState​​​,所以React通过将一个Hook对象挂载在​​memorizedStated​​​上来保存函数组件的​​state​​重点关注​​memoizedState​​​和​​next​​​​memoizedState​​是用来记录当前​​useState​​应该返回的结果的​​query​​:缓存队列,存储多次更新行为​​next​​:指向下一次​​useState​​对应的Hook对象。renderWithHooks​​renderWithHooks的运行过程如下:​​// ReactFiberHooks.jsexport function renderWithHooks( current: Fiber | null, workInProgress: Fiber, Component: any, props: any, refOrContext: any, nextRenderExpirationTime: ExpirationTime,): any { renderExpirationTime = nextRenderExpirationTime; currentlyRenderingFiber = workInProgress; // 如果current的值为空,说明还没有hook对象被挂载 // 而根据hook对象结构可知,current.memoizedState指向下一个current nextCurrentHook = current !== null ? current.memoizedState : null; // 用nextCurrentHook的值来区分mount和update,设置不同的dispatcher ReactCurrentDispatcher.current = nextCurrentHook === null // 初始化时 ? HooksDispatcherOnMount // 更新时 : HooksDispatcherOnUpdate; // 此时已经有了新的dispatcher,在调用Component时就可以拿到新的对象 let children = Component(props, refOrContext); // 重置 ReactCurrentDispatcher.current = ContextOnlyDispatcher; const renderedWork: Fiber = (currentlyRenderingFiber: any); // 更新memoizedState和updateQueue renderedWork.memoizedState = firstWorkInProgressHook; renderedWork.updateQueue = (componentUpdateQueue: any); /** 省略与本文无关的部分代码,便于理解 **/}初始化时核心:创建一个新的hook,初始化state, 并绑定触发器 初始化阶段​​ReactCurrentDispatcher.current​​​ 会指向​​HooksDispatcherOnMount​​ 对象// ReactFiberHooks.js const HooksDispatcherOnMount: Dispatcher = {/** 省略其它Hooks **/ useState: mountState,}; // 所以调用useState(0)返回的就是HooksDispatcherOnMount.useState(0),也就是mountState(0)function mountState<S>( initialState: (() => S) | S,): [S, Dispatch<BasicStateAction<S>>] { // 访问Hook链表的下一个节点,获取到新的Hook对象 const hook = mountWorkInProgressHook();//如果入参是function则会调用,但是不提供参数 if (typeof initialState === 'function') { initialState = initialState(); }// 进行state的初始化工作 hook.memoizedState = hook.baseState = initialState;// 进行queue的初始化工作 const queue = (hook.queue = { last: null, dispatch: null, eagerReducer: basicStateReducer, // useState使用基础reducer eagerState: (initialState: any), }); // 返回触发器 const dispatch: Dispatch<BasicStateAction<S>,> = (queue.dispatch = (dispatchAction.bind( null, //绑定当前fiber结点和queue ((currentlyRenderingFiber: any): Fiber), queue, )); // 返回初始state和触发器 return [hook.memoizedState, dispatch];} // 对于useState触发的update action来说(假设useState里面都传的变量),basicStateReducer就是直接返回action的值function basicStateReducer<S>(state: S, action: BasicStateAction<S>): S { return typeof action === 'function' ? action(state) : action;}更新函数 ​​dispatchAction​​function dispatchAction<S, A>( fiber: Fiber, queue: UpdateQueue<S, A>, action: A,) { /** 省略Fiber调度相关代码 **/ // 创建新的新的update, action就是我们setCount里面的值(count+1, count+2, count+3…) const update: Update<S, A> = { expirationTime, action, eagerReducer: null, eagerState: null, next: null, }; // 重点:构建query // queue.last是最近的一次更新,然后last.next开始是每一次的action const last = queue.last; if (last === null) { // 只有一个update, 自己指自己-形成环 update.next = update; } else { const first = last.next; if (first !== null) { update.next = first; } last.next = update; } queue.last = update; /** 省略特殊情况相关代码 **/ // 创建一个更新任务 scheduleWork(fiber, expirationTime); }​​dispatchAction​​中维护了一份query的数据结构。query是一个有环链表,规则:query.last指向最近一次更新last.next指向第一次更新后面就依次类推,最终倒数第二次更新指向last,形成一个环。所以每次插入新update时,就需要将原来的first指向query.last.next。再将update指向query.next,最后将query.last指向update.更新时核心:获取该Hook对象中的 queue,内部存有本次更新的一系列数据,进行更新更新阶段 ​​ReactCurrentDispatcher.current​​​ 会指向​​HooksDispatcherOnUpdate​​对象// ReactFiberHooks.js // 所以调用useState(0)返回的就是HooksDispatcherOnUpdate.useState(0),也就是updateReducer(basicStateReducer, 0) const HooksDispatcherOnUpdate: Dispatcher = { /** 省略其它Hooks **/ useState: updateState,} function updateState(initialState) { return updateReducer(basicStateReducer, initialState);} // 可以看到updateReducer的过程与传的initalState已经无关了,所以初始值只在第一次被使用 // 为了方便阅读,删去了一些无关代码// 查看完整代码:https://github.com/facebook/react/blob/487f4bf2ee7c86176637544c5473328f96ca0ba2/packages/react-reconciler/src/ReactFiberHooks.js#L606function updateReducer(reducer, initialArg, init) {// 获取初始化时的 hook const hook = updateWorkInProgressHook(); const queue = hook.queue; // 开始渲染更新 if (numberOfReRenders > 0) { const dispatch = queue.dispatch; if (renderPhaseUpdates !== null) { // 获取Hook对象上的 queue,内部存有本次更新的一系列数据 const firstRenderPhaseUpdate = renderPhaseUpdates.get(queue); if (firstRenderPhaseUpdate !== undefined) { renderPhaseUpdates.delete(queue); let newState = hook.memoizedState; let update = firstRenderPhaseUpdate; // 获取更新后的state do { const action = update.action; // 此时的reducer是basicStateReducer,直接返回action的值 newState = reducer(newState, action); update = update.next; } while (update !== null); // 对 更新hook.memoized hook.memoizedState = newState; // 返回新的 state,及更新 hook 的 dispatch 方法 return [newState, dispatch]; } } } // 对于useState触发的update action来说(假设useState里面都传的变量),basicStateReducer就是直接返回action的值function basicStateReducer<S>(state: S, action: BasicStateAction<S>): S { return typeof action === 'function' ? action(state) : action;}总结单个hooks的更新行为全都挂在Hooks.queue下,所以能够管理好queue的核心就在于初始化queue - mountState维护queue - dispatchAction更新queue - updateReducer结合示例代码:当我们第一次调用​​[count, setCount] = useState(0)​​时,创建一个queue每一次调用​​setCount(x)​​,就dispach一个内容为x的action(action的表现为:将count设为x),action存储在queue中,以前面讲述的有环链表规则来维护这些action最终在​​updateReducer​​中被调用,更新到​​memorizedState​​上,使我们能够获取到最新的state值。​​文章就分享到这,欢迎关注“前端大神之路”​​​​
收藏
评论
分享
举报
上一篇:React中使用Suspense
下一篇:HTTP协议大全
提问和评论都可以,用心的回复会被更多人看到
评论
发布评论
全部评论
()
最热
最新
相关文章
react 17源码学习 (5) useState原理 How useState works
React
简单的 useState 实现
简单的 useState 实现本文写于 2020 年 10 月 21 日以下是一段非常简单的 React 代码:const App = () => {  const [n, setN] = useState(0);  return ({n}setN(x => x + 1)}>+1);}React.render(, rootElement)这样的用法和以往的 setSta
useState
Hooks中的useState
Hooks中的useState React的数据是自顶向下单向流动的,即从父组件到子组件中,组件的数据存储在props和state中,实际上在任何应用中,数据都是必不可少的,我们需要直接的改变页面上一块的区域来使得视图的刷新,或者间接地改变其他地方的数据,在React中就使用props和state两
复用
代码复用
作用域
从原理上解读useState钩子函数
原生useState具有的几个特点返回一个数组,数组的第一个参数值是状态值,第二个参数值是设置状态的方法。useState会判断传入的state是初始值还是已存在
react.js
数组
自定义
初始状态
setState 和 useState 的区别
1.获取修改后的值 setState this.setState({ count: this.state.count + 1 }, () => { console.log(this.state.count); // 这里是监控到的最新值 }) useState 通过 useEffect 实现 con
回调函数
编程
react的useState源码分析
前言简单说下为什么React选择函数式组件,主要是class组件比较冗余、生命周期函数写法不友好,骚写法多,functional组件更符合React编程思想等等等。更具体的可以拜读dan大神的blog。其中Functioncomponentscapturetherenderedvalues这句十分精辟的道出函数式组件的优势。但是在16.8之前react的函数式组件十分羸弱,基本只能作用于纯展示组件
react
react hooks useState
react-hooks写法直接用函数形式完成()=>{setCount(count+1)}useState是react自带的一个h
数组
条件判断
[React] useState with Typescript
function useState<S>( initialState: S | (() => S), ): [S, Dispatch<SetStateAction<S>>] Example: function useDarkMode() { // ... const returnValue: [st
sed
React
React HookAPI useState()
useState是React Hook中声明变量的方法,useState提供了一个获取方法、一个设置方法 import React from 'react'; // useState是React的方法使用useState方法时要提前引入React依赖包 const [state, setState] ...
默认值
回调函数
依赖包
数组
useState & useEffect
useState & useEffectuseState , useEffect, useRef, React Hooks, react, js, pagination
useState
useEffect
React Hooks
React
js
js 模拟React Hooks的useState
2021年02月23日,原生js模拟hooks的useState let _state = []; let index = 0; const myUseState = (initialState) => { var currentIndex = index; // 保存index _state[cu ...
原生js
React中useState,useEffect的使用
import React, { useState, useEffect } from 'react' export default function App() { const [count, setCount] = useState(0) useEffect(() => { document.ti ...
useState
useEffect
数组
钩子函数
JAVA
useState用法指南
useStateconst [state, setState] = useState(initialState);返回一个 state,以及更新 state 的函数。在初始渲染期间,返回的状态 (state) 与传入的第一个参数 (initialState) 值相同。setState 函数用于更新 state。它接收一个新的 state 值并将组件的一次重新渲染加入队列。setStat...
react
函数式
数组
表单
其他
ReactJS Hook之useState
声明 State 变量在 class 中,我们通过在构造函数中设置 this.state 为 { count: 0 } 来初始化 count state 为 0:class Example extends React.C们直接在组
构造函数
赋值
函数调用
useState 使用方法
import React,{useState} from 'react' export default function ComplexHookState() { const [friends, setFriends] = useState(['李雷','科比']) const [students,
好友列表
react Hooks 封装 useState
import { useState } from "react" /** * 使用表单 * @param props */ export default function useForm(initialState: any) { const [state, setState] = useState( ...
表单
IT
【React Hooks 系列】useState
大家好,我是前端西瓜哥。React 在 16.8 引入了 Hooks 概念,赋予了函数组件以 “魔法”,也能
引用类型
函数返回
数组
手写useState与useEffect
手写useState与useEffect useState与useEffect是驱动React hooks运行的基础,useState用于管理状态,useEffect用以处理副作用,通过手写简单的useState与useEffect来理解其运行原理。 useState 一个简单的useState的使
数组
css
自定义
React: hook(1) useState
react
react.js
前端早茶
关注
私信
分类列表
# Node.js2篇
# React2篇
近期文章
1.PHP正则表达式
2.虚拟化云计算-podman学习笔记
3.图解 Promise 实现原理(二)—— Promise 链式调用
4.全方位认知MRO工业品
5.网络安全与网站安全及计算机安全:如何使用Kali Linux进行MS08-067安全演练
文章目录
一、初始化
二、更新时
三、 了解useState
useState的引入
useState的实现
四. 核心步骤分析
Hook对象的结构如下:
renderWithHooks
总结
每日签到
新人福利
举报文章
请选择举报类型
内容侵权
涉嫌营销
内容抄袭
违法信息
其他
具体原因
包含不真实信息
涉及个人隐私
原文链接(必填)
补充说明
0/200
上传截图
格式支持JPEG/PNG/JPG,图片不超过1.9M
取消
确认
已经收到您得举报信息,我们会尽快审核
收藏
评论
分享
51CTO首页
内容精选
博客
学堂
精培
企业培训
CTO训练营
智能汽车
开源基础软件社区
WOT全球技术创新大会
公众号矩阵
移动端
免费课程
课程排行
直播课
软考学堂
精品班
厂商认证
IT技术
2023年软考
PMP项目管理
在线学习
企业服务
CTO训练营
技术经理研习营
LeaTech峰会
文章
资源
问答
开源课堂
专栏
直播
51CTO
开源基础软件社区
51CTO技术栈
51CTO官微
51CTO学堂
51CTO博客
CTO训练营
开源基础软件社区订阅号
51CTO学堂APP
51CTO学堂企业版APP
开源基础软件社区视频号
51CTO博客
首页
关注
排行榜
订阅专栏
搜索历史
清空
热门搜索
查看【
】的结果
新人福利 写文章
创作中心
登录注册
Copyright 2005-2023 51CTO.COM
版权所有 京ICP证060544号
关于我们
官方博客
全部文章
热门标签
班级博客
了解我们
在线客服
网站地图
意见反馈
友情链接
开源基础软件社区
51CTO学堂
51CTO
汽车开发者社区