React源码解析之React.createContext()
前言:
由于childContext
在React17
中会被废弃,所以不去分析它了,主要是新 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
不会生效,Consumer
的value
显示空值
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
,严重影响了性能
(完)
相关推荐
游走的豚鼠君 2020-11-10
81417707 2020-10-30
ctg 2020-10-14
小飞侠V 2020-09-25
PncLogon 2020-09-24
jipengx 2020-09-10
颤抖吧腿子 2020-09-04
wwzaqw 2020-09-04
maple00 2020-09-02
青蓝 2020-08-26
罗忠浩 2020-08-16
liduote 2020-08-13
不知道该写啥QAQ 2020-08-02
pengruiyu 2020-08-01
wmd看海 2020-07-27
孝平 2020-07-18
Eduenth 2020-07-05
iftrueIloveit 2020-07-04