实现一个 Mini React:核心功能详解 - useContext 的实现, 从底层理解运行机制

131 阅读2分钟

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

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

  1. 完成了useSyncExternalStore的实现, 从底层理解运行机制。

有兴趣的可以点这里查看useSyncExternalStore的实现, 从底层理解运行机制

这一篇,我们从0-1 实现 useContext,先来一波代码演示一下平日如何使用的,

const MyContext = createContext('default value')

function A() {
    return (
        <MyContext.Provider value="A value">
            <B />
        </MyContext.Provider>
    )
}

function B() {
    return (
        <MyContext.Provider value="B value">
            <C />
        </MyContext.Provier>
    )
}

function C() {
    const value = useContext(MyContext)
    return <div>{value}</div> // B 的值
}

function E() {
    const value = useContext(MyContext)
    return <div>{value}</div> // default value
}

根据上面的代码可以知道,

  1. createContext 返回一个组件 context, context 有一个属性即Provider 函数,所以可以使用 <MyContext.Provider> 进行渲染,即函数组件
  2. 每次使用<MyContext.Provider value="any value">, 包裹里面的组件可以使用提供的value值。
  3. 但是每次用了之后,需要回退原始值,上面的代码里面的E组件就是一个独立组件,如果不这样做,E组件获取的就是B value, 而不是default value

实现 createContext

fucntion createContext(defaultValue) {
    const context = {
        defaultValue,
        Provider: function({value,  children}) {
            const fiber = workInProgress // 当前渲染的 fiber node
            
            const previousValue = fiber.contexts ? fiber.contexts[context] : undefined
            if (!fiber.contexts) {
                fiber.contexts = {}
            }
            fiber.contexts[context] = value
            
            //  renderComponent已经之前的文章里实现过了,有兴趣的可以查看
            const renderChildren = renderComponent(children)
            
            // 恢复之前的value
            if (previousValue === undefined) 
                delete fiber.contexts[context]
            else 
                fiber.contexts[context] = previousValue
        }
        
        return renderChildren
    }
    return context
}

实现 useContext

function useContext(context) {
    const fiber = workInProgress
    
    let currentContext = context.defaultValue;
    let currentFiber = fiber
    
    while(currentFiber) {
        if (currentFiber.contexts && currentFiber.contexts[context]!==undefined) {
            currentContext = currentFiber.contexts[context]
            break
        }
        currentFiber = currentFiber.parent // 回溯父fiber进行查找
    }
    
    return currentContext
}

createContext / useContext 即从fiber进行遍历获取最近的context, 如果没有则使用默认值。从代码层面你可以了解,总是从子->父顺序层层进行查看,直至第一个context匹配。

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

建议

我们的源码层面没有使用堆栈结构解决内嵌等情况,如果有兴趣的小伙伴可以考虑实现一下。官方使用的就是堆栈结构。

那么下一篇将从0-1实现开发 useDeferredValue 。

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

啥也不是,散会。