实现一个 Mini React:核心功能详解 - 重构useState的实现 (2)

165 阅读2分钟

xdm,又要到饭了,又更新代码了!

总结一下上一篇完成的内容,

  1. 改进了useState的实现, 可以配合fiber进行渲染。

有兴趣的可以点这里查看重构useState的实现 (1)

useState的实现还没有完美,即现在每次的调用setState 都会重新渲染。这不是我们想要的,我们想要的多次更新merge只更新最新的那次数据,不是现在的每次都会单独更新一次。

添加一个批量更新是一个非常简单的,基于js单线程的特质,可以添加一个锁机制来完成。 使用setState,会调用 requestHostCallback ,这个函数会绘制fiber树,接着根据更新好的fiber树,进行ui的更新。那么这个函数里面添加一个锁,即只有当前的锁===false, 才更新fiber/ui。

用户即使一个事件循环内多次调用setState也只会有一次fiber/ui 的更新。但是setState会收集所有的更新逻辑。因为setState 的更新默认属于异步,所以真正更新fiber/ui和批量添加更新逻辑不在一个循环。这是现在的useState代码,

function useState(initial) {
    // workInProgress , 当前fiber
   
    
    const oldHook = workInProgress.alternate && workInProgress.alternate.hooks && workInProgress.alternate.hooks[workInProgress.currentHook]
    
    const hook = {
        state: oldHook.state || initial
        queue: []
    }
    
    
    const actions = oldHook ? oldHook.queue : []
    actions.forEach(action => {
        hook.state = typeof action === 'function'action(hook.state) : action
    })
    
    if (oldHook)
        oldHook.queue = []
        
    const setState = action => {
        if (typeof action === 'function') {
            hook.queue.push(prevState=>action(prevState))
        } else hook.queue.push(action)
        
        requestHostCallback(workLoop)
    }
    
    workInProgress.hooks.push(hook)
    workInProgress.currentHook++
    
    return [hook.state, setState]
}

上面的代码每次调用setState都会调用requestHostCallback(workLoop) 函数进行 fiber / ui 的更新,那么添加锁的位置就是在 requestHostCallback 函数里面。

// 工作循环 
let workLoopScheduled = false;
function requestHostCallback(callback) { 
    if (workLoopScheduled) return; 
    // 已经调度,直接返回 
    workLoopScheduled = true; 
    // 标记为已调度 
    if (typeof requestIdleCallback !== 'undefined') { 
        requestIdleCallback(deadline => { 
            workLoopScheduled = false; 
            // 渲染循环开始时重置标志 
            callback(deadline); 
        }); 
    } else {
        // 浏览器不支持 requestIdleCallback 时的回退方案 
        setTimeout(() => { 
            workLoopScheduled = false; 
            // 渲染循环开始时重置标志 
            callback({ timeRemaining: () => Infinity }); }, 0); 
    } 
} 

function workLoop(deadline) { 
    while (nextUnitOfWork && deadline.timeRemaining() > 0) { 
        nextUnitOfWork = performUnitOfWork(nextUnitOfWork); 
    } 
    if (nextUnitOfWork) { 
        requestHostCallback(workLoop); 
    } 
    else { 
        commitRoot(); 
    } 
    workLoopScheduled = false; 
}

这样就利用了锁机制以及js单线程实现了useState批量更新的机制。

这一章节实现了useState函数批量更新实现。

下一篇将开始重构之前的useEffect hook

如果这样的长度/强度你觉得可以接受,觉得有帮助,可以继续阅读下一篇,实现一个 Mini React:核心功能详解 - 重构useEffect的实现 。

如果文章对你有帮助,请点个赞支持一下!

啥也不是,散会。