React源码解析之ReactcreateContext

38次阅读

共计 4793 个字符,预计需要花费 12 分钟才能阅读完成。

前言:
由于 childContextReact17中会被废弃,所以不去分析它了,主要是新 API— —createContext()的讲解

一、React.createContext()

作用:
方便祖先组件与后代组件(中间隔了好多层组件)传值

使用:
context.js:

import React from 'react';

const contextTestOne={
  name:'chen',
  length:22,
}

export const wrapContext=React.createContext(contextTestOne.name)

祖先组件:

import {wrapContext} from '@/utils/context';

const Father=props=>{return (<wrapContext.Provider value={'this is provider'}>
     <Child />
</wrapContext.Provider>)
}

子孙组件:

import {wrapContext} from '@/utils/context';

const getProviderValue=()=>{return <wrapContext.Consumer>{value=><span>{value}</span>}</wrapContext.Consumer>
}

const Child=props=>{
return (getProviderValue()
  );
}

结果:

注意:
undefined 传递给 <Provider>value时,createContext中的 defaultValue 不会生效,Consumervalue 显示空值

React 官方文档:
https://zh-hans.reactjs.org/docs/context.html#contextprovider

源码:

/**
 * Copyright (c) Facebook, Inc. and its affiliates.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 *
 * @flow
 */

import {REACT_PROVIDER_TYPE, REACT_CONTEXT_TYPE} from 'shared/ReactSymbols';

import type {ReactContext} from 'shared/ReactTypes';

import warningWithoutStack from 'shared/warningWithoutStack';
import warning from 'shared/warning';

export function createContext<T>(
  defaultValue: T,
  // 使用 Object.is()计算新老 context 的差异
  calculateChangedBits: ?(a: T, b: T) => number,
): ReactContext<T> {if (calculateChangedBits === undefined) {calculateChangedBits = null;} else {
    // 不看
    if (__DEV__) {
      warningWithoutStack(
        calculateChangedBits === null ||
          typeof calculateChangedBits === 'function',
        'createContext: Expected the optional second argument to be a' +
          'function. Instead received: %s',
        calculateChangedBits,
      );
    }
  }

  const context: ReactContext<T> = {
    // 还是那句话,ReactContext 中的 $$typeof 是
    // 作为 createElement 中的属性 type 中的对象进行存储的,并不是 ReactElement 的 $$typeof
    $$typeof: REACT_CONTEXT_TYPE,
    _calculateChangedBits: calculateChangedBits,
    // 作为支持多个并发渲染器的解决方法,我们将一些渲染器分类为主要渲染器,将其他渲染器分类为辅助渲染器。// As a workaround to support multiple concurrent renderers, we categorize
    // some renderers as primary and others as secondary.

    // 我们只希望最多有两个并发渲染器:React Native(主要)和 Fabric(次要);
    // React DOM(主要)和 React ART(次要)。// 辅助渲染器将自己的 context 的 value 存储在单独的字段中。// We only expect
    // there to be two concurrent renderers at most: React Native (primary) and
    // Fabric (secondary); React DOM (primary) and React ART (secondary).
    // Secondary renderers store their context values on separate fields.

    //<Provider value={xxx}> 中的 value 就是赋值给_currentValue 的

    // 也就是说_currentValue 和_currentValue2 作用是一样的,只是分别给主渲染器和辅助渲染器使用
    _currentValue: defaultValue,
    _currentValue2: defaultValue,
    // Used to track how many concurrent renderers this context currently
    // supports within in a single renderer. Such as parallel server rendering.

    // 用来追踪该 context 的并发渲染器的数量
    _threadCount: 0,
    // These are circular
    Provider: (null: any),
    Consumer: (null: any),
  };
  //const obj={}
  //obj.provider._obj = obj
  context.Provider = {
    $$typeof: REACT_PROVIDER_TYPE,
    _context: context,
  };

  let hasWarnedAboutUsingNestedContextConsumers = false;
  let hasWarnedAboutUsingConsumerProvider = false;
  // 不看
  if (__DEV__) {
    // A separate object, but proxies back to the original context object for
    // backwards compatibility. It has a different $$typeof, so we can properly
    // warn for the incorrect usage of Context as a Consumer.
    const Consumer = {
      $$typeof: REACT_CONTEXT_TYPE,
      _context: context,
      _calculateChangedBits: context._calculateChangedBits,
    };
    // $FlowFixMe: Flow complains about not setting a value, which is intentional here
    Object.defineProperties(Consumer, {
      Provider: {get() {if (!hasWarnedAboutUsingConsumerProvider) {
            hasWarnedAboutUsingConsumerProvider = true;
            warning(
              false,
              'Rendering <Context.Consumer.Provider> is not supported and will be removed in' +
                'a future major release. Did you mean to render <Context.Provider> instead?',
            );
          }
          return context.Provider;
        },
        set(_Provider) {context.Provider = _Provider;},
      },
      _currentValue: {get() {return context._currentValue;},
        set(_currentValue) {context._currentValue = _currentValue;},
      },
      _currentValue2: {get() {return context._currentValue2;},
        set(_currentValue2) {context._currentValue2 = _currentValue2;},
      },
      _threadCount: {get() {return context._threadCount;},
        set(_threadCount) {context._threadCount = _threadCount;},
      },
      Consumer: {get() {if (!hasWarnedAboutUsingNestedContextConsumers) {
            hasWarnedAboutUsingNestedContextConsumers = true;
            warning(
              false,
              'Rendering <Context.Consumer.Consumer> is not supported and will be removed in' +
                'a future major release. Did you mean to render <Context.Consumer> instead?',
            );
          }
          return context.Consumer;
        },
      },
    });
    // $FlowFixMe: Flow complains about missing properties because it doesn't understand defineProperty
    context.Consumer = Consumer;
  }

  else {//const obj={}
    //obj.consumer=obj
    // 也就是 Consumber 对象指向 React.Context 对象

    // 在 <Consumer> 进行渲染时,为了保证 Consumer 拿到最新的值,// 直接让 Consumer=React.Context,// React.Context 中的_currentValue 已经被 <Provider> 的 value 给赋值了
    // 所以 Consumer 能立即拿到最新的值
    context.Consumer = context;
  }
  // 不看
  if (__DEV__) {
    context._currentRenderer = null;
    context._currentRenderer2 = null;
  }

  return context;
}

解析:
不看 __DEV__ 的话,还是挺简单的,需要注意的是 context.Consumer = context,让<Consumer> 等于 React.context,这样能立即拿到<Provider> 提供的最新值


二、为什么要弃用 childContext
因为 childContext 对下层的组件影响太大了,即使子孙组件没有用到childContext,子孙组件仍然要进行Update,严重影响了性能


(完)

正文完
 0