Preface:
Since childContext will be discarded in React17, we will not analyze it, mainly the explanation of the new API, createContext().
1. React.createContext()
Effect:
Convenient transfer of values between ancestor components and descendant components (separated by many layers of components)
Use:
context.js:
import React from 'react'; const contextTestOne={ name:'chen', length:22, } export const wrapContext=React.createContext(contextTestOne.name)
Ancestral Components:
import { wrapContext } from '@/utils/context'; const Father=props=>{ return (<wrapContext.Provider value={'this is provider'}> <Child /> </wrapContext.Provider>) }
The descendant component:
import { wrapContext } from '@/utils/context'; const getProviderValue=()=>{ return <wrapContext.Consumer>{value=><span>{value}</span>}</wrapContext.Consumer> } const Child=props=>{ return ( getProviderValue() ); }
Result:
Be careful:
When undefined is passed to the value of <Provider>, defaultValue in createContext does not take effect, and Consumer's value displays null values.
React official documents:
https://zh-hans.reactjs.org/docs/context.html#contextprovider
Source code:
/** * 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, //Computing the differences between old and new context s using Object.is() calculateChangedBits: ?(a: T, b: T) => number, ): ReactContext<T> { if (calculateChangedBits === undefined) { calculateChangedBits = null; } else { //No look 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> = { //Again, $typeof in ReactContext is // Stored as an object in the attribute type in createElement, it's not $typeof in ReactElement. $$typeof: REACT_CONTEXT_TYPE, _calculateChangedBits: calculateChangedBits, //As a solution to support multiple concurrent renderers, we classify some renderers as main renderers and others as auxiliary renderers. // As a workaround to support multiple concurrent renderers, we categorize // some renderers as primary and others as secondary. //We just want to have at most two concurrent renderers: React Native (primary) and Fabric (secondary); // React DOM (primary) and React ART (secondary). // The auxiliary renderer stores its context value in a separate field. // 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. //Value in <Provider value={xxx}> is assigned to _current Value. //That is to say _currentValue and _currentValue2 work the same way, except for the main and auxiliary renderers, respectively. _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. //The number of concurrent renderers used to track the 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; //No look 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 //That is, the Consumber object points to the React.Context object //In order to ensure that Consumer gets the latest value when rendering <Consumer>, //Let Consumer=React.Context directly, // The _current Value in React.Context has been assigned to the value of <Provider>. //So Consumer can get the latest value right away. context.Consumer = context; } //No look if (__DEV__) { context._currentRenderer = null; context._currentRenderer2 = null; } return context; }
Analysis:
Without looking at _DEV_, it's still quite simple. It's important to note that context.Consumer = context, so that < Consumer > equals React.context, so that you can get the latest value provided by < Provider > immediately.
2. Why abandon childContext?
Because childContext has a great impact on the underlying components, even if the child Context is not used by the descendant components, the descendant components still need to Update, seriously affecting performance.
(End)