代码实现的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)]

}
调试

调试+全代码

相关推荐
真的很上进2 小时前
如何借助 Babel+TS+ESLint 构建现代 JS 工程环境?
java·前端·javascript·css·react.js·vue·html
wakangda13 小时前
React Native 集成原生Android功能
javascript·react native·react.js
秃头女孩y19 小时前
【React中最优雅的异步请求】
javascript·vue.js·react.js
前端小小王1 天前
React Hooks
前端·javascript·react.js
迷途小码农零零发1 天前
react中使用ResizeObserver来观察元素的size变化
前端·javascript·react.js
不是鱼1 天前
构建React基础及理解与Vue的区别
前端·vue.js·react.js
飞翔的渴望1 天前
antd3升级antd5总结
前端·react.js·ant design
爱喝奶茶的企鹅1 天前
Next.js 14 路由进阶:从约定式到动态路由的最佳实践
react.js
╰つ゛木槿2 天前
深入了解 React:从入门到高级应用
前端·react.js·前端框架
用户30587584891252 天前
Connected-react-router核心思路实现
react.js