React context 基本原理

5,480 阅读2分钟

React 图解完整版:

链接:pan.baidu.com/s/1EfTWzqC4… 密码:8nqr

仓库:github.com/BUPTlhuanyu…

🍀ReactContext.js: createContext 创建 context

传入的参数为:

defaultValue            provider提供的默认值
calculateChangedBits    自定义的 vakue 比较函数,默认情况下如果传入 provider 的 value 值没有变化则返回 0,否则返回 1073741823

定义context:

const context: ReactContext<T> = {
    $$typeof: REACT_CONTEXT_TYPE,
    _calculateChangedBits: calculateChangedBits,
    _currentValue: defaultValue,
    _currentValue2: defaultValue,
    // These are circular
    // 循环引用
    Provider: (null: any),
    Consumer: (null: any),
};

定义context.Provider:

context.Provider = {
    $$typeof: REACT_PROVIDER_TYPE,
    _context: context,//引用context
};

生产环境下定义context.Consumer

context.Consumer = context 

🍀 context 跨组件通信原理

用于保存 Provider value 的栈

在调用 createContext 的过程中其中 valueStack 用于保存所有 context 的 provider 提供的 value,其中还包括一些 namespace 等内容。在 provider 上提供的值,任意位置的 consumer 组件以及通过 Class.contextType 赋予类组件中的 this.context 都可以通过 cursor.current 去取到最近的 provider 提供的。React 利用栈这个数据结构以及遍历 fiber 树的方式,保证了消费者能正确获取到 context.provider 的值。

react 对上述栈的封装

  1. push 方法将 cursor.current 的值推到 valueStack 的栈顶,然后把当前 provider 节点的变化了的 value 值放到 cursor.current 中。

  2. pop 方法则将 cursor.current 的值替换成 valueStack 栈顶的值,然后栈顶的值重置为 null,接着 index--。

react 在 renderRoot 阶段的遍历方式以及对上述栈的处理时机

  1. performUnitOfWork 每次都会在 completeUnitOfWork 之前调用。而在performUnitOfWork中会调用updateContextProvider,该函数会调用pushProvider将 cursor.current放到栈顶,然后新的value放到cursor.current上。
  2. 接着在遇到 context.consumer 的时候,会去读取这个 cursor.current 的值,给到 Consumer 的 children,也就是一个函数,接收 provider 的 value。在遇到 classComponent 的时候,同样会读取 cursor.current 的值然后赋值给组件实例的 context。
  3. 最后调用completeUnitOfWork的时候又会去popPrivider,pop会将栈顶的value恢复到cursor.current。所以永远取得的是最近的priovider的value。

可以结合下面的图观察一下,per里面push,com中pop。push和pop之间取得的value就是push进去的。