代码实现的React的useState

简介

useState是react里面最重要的hook之一,本文我们主要是基于react原理,实现一个在改变state后能将最新的状态React渲染到页面这样一个功能。

前置知识

正常我们的一个函数组件是这样的

我们前置知识主要涉及fiber,React是双缓存,React更新调度,下面代码就主要定义了一下这些知识

csharp 复制代码
//react中的虚拟dom就是叫fiber
const fiber = {
    node:App,
    memoizedState:null,
}

//React是双缓存设计,一个是更新工作时使用的fiber,另外一个是负责渲染展示的fiber
let workInprogressFiber = null

//判断是不是初次渲染
let isMount = true


//调度React更新的函数,主要是更新node节点
function schedule(){
    workInprogressFiber = fiber.memoizedState

    //这里就触发了node节点的更新
    fiber.node()    

    isMount = false
}

通过上面的定义,我们就有了一个最简单的React运行环境

useState
1、初始化hooks,记录每个useState的使用顺序

在我们每一次渲染的时候, 我们会使用isMount来判断是不是第一次渲染,有没有存在的fiber链。有存在的fiber就直接使用,没有存在的我们我把当前hooks添加到上一个hooks的后面。在这个过程中我们使用了workInprogressFiber,他相对于一个滑块,能够一直在上面获取到我们当前要使用的hooks

csharp 复制代码
function useState(initValue){

    let hooks = null

    if(isMount){
        //初次render,
        hooks = {
            memoizedState:initValue,
            queue:null,//后面存放dispath(react的第二个函数)
            next:null,
        }

        //判断是不是起始节点
        if(fiber.memoizedState){
            //前面已经有了节点,workInprogressFiber指向的是上一次进入useState的hooks地址
            //将上一个hooks的指针指向当前hook,这样就能记录我们的每个useState的调用顺序
            workInprogressFiber.next = hooks
        }else{
            //第一次进fiber,fiber还没有任何状态,
            fiber.memoizedState = hooks
        }
        //workInprogressFiber指向下一个指针
        workInprogressFiber = hooks


    }else{
        //在render阶段,我们调度更新的时候会将workInprogressFiber指向fiber.memoizedState,所以更新的时候就可以直接拿到需要的hooks
        hooks = workInprogressFiber
        //workInprogressFiber指向下面一个hooks下次进入就可以直接使用
        workInprogressFiber = workInprogressFiber.next
    }
    
    ...
    ...

}
2、useState的第二返回值,更新函数setState的实现

我们使用setState改变state的值时,都会调用setState,因此在调用setState的时候会调用schedule函数,开始更新。并且setState是可以多次调用的,我们使用一个环形链表,记录调用顺序。后面在useState中依次执行

arduino 复制代码
//useState的第二个返回值
function setState(queue,action){

    //这里的action可以是函数也可以是值
    const update = {
        action:action,
        next:null
    }

    if(!queue){
        update.next = update
        
    }else{
        update.next = queue.next
        queue.next = update

    }

    queue = update
    //模拟开始调度
    schedule()
}
3、useState使用setState函数

这里主要是遍历queue,得到最后的值。需要再每次遍历过后清空queue队列。将最后的值复制到hooks

java 复制代码
function useState(initValue){

    let hooks = null

    if(isMount){
    ...
    }else{
    ...    
    }
    
    let baseState = hooks.memoizedState

    if(hooks?.queue){
        //遍历queue,执行完所以的setState
        const  firstNode = hooks?.queue.next
        do{
            const action = firstNode.action
            baseState = typeof action === 'function' ? action(baseState):baseState
            firstNode = firstNode.next
        }
        while(firstNode !== hooks?.queue.next)
        
         //清空queue
        hooks.queue.pendding = null

    }
    //将值存回hooks
    hooks.memoizedState = baseState
    //使用bind函数将hooks.queue传入,后面就不必在传hooks.queue
    return [baseState,setState.bind(null,hooks.queue)]

}
调试

调试+全代码

相关推荐
半个落月7 小时前
React useRef 详解:DOM 引用、持久化值与 Worker 实例
前端·react.js
光影少年10 小时前
React Native网络、存储与原生能力
前端·react native·react.js
To_OC10 小时前
绕开层层 props 搬运:我把 useContext 和自定义 Hook 跑通了
前端·react.js·前端框架
张元清13 小时前
React useMount Hook:只在挂载时执行一次的正确姿势 (2026)
javascript·react.js
只一14 小时前
React 性能优化精讲:useCallback 与 useMemo 彻底吃透(附实战案例)
前端·react.js
YIAN14 小时前
吃透这 5 个核心点,你的 React+TS 代码直接上一个台阶
前端·react.js·typescript
小林ixn14 小时前
从混乱到清晰:项目架构与自定义 Hook 的双重实践
react.js·架构·前端框架
breeze jiang16 小时前
React + WebGPU 在浏览器运行 DeepSeek:从 Worker 通信到流式生成
前端·javascript·react.js
小林ixn16 小时前
前端卡顿终结者:用 useRef 把 Web Worker 请进 React 项目
前端·react.js·前端框架
sunly_16 小时前
React 三个重要概念:Ref、Props、State 详解
前端·javascript·react.js