前言:
因爲childContext
在React17
中會被廢棄,因此不去分析它了,主要是新 API— —createContext()
的講解html
1、React.createContext()react
做用:
方便祖先組件與後代組件(中間隔了好多層組件)傳值併發
使用:
context.js:ide
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>) }
子孫組件:this
import { wrapContext } from '@/utils/context'; const getProviderValue=()=>{ return <wrapContext.Consumer>{value=><span>{value}</span>}</wrapContext.Consumer> } const Child=props=>{ return ( getProviderValue() ); }
結果:spa
注意:
將undefined
傳遞給<Provider>
的value
時,createContext
中的defaultValue
不會生效,Consumer
的value
顯示空值code
React 官方文檔:
https://zh-hans.reactjs.org/docs/context.html#contextproviderserver
源碼:htm
/** * 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>
提供的最新值
2、爲何要棄用childContext
?
由於childContext
對下層的組件影響太大了,即便子孫組件沒有用到childContext
,子孫組件仍然要進行Update
,嚴重影響了性能
(完)