1 )概述
childContextTypes
childContextTypes
在react17 这个大版本发布的时候被废弃createContext
这个API
1 )示例演示
这个示例,演示了 新旧 两个 api 的用法
import React from 'react'
import PropTypes from 'prop-types'
const { Provider, Consumer } = React.createContext('default')
// 定义一个父组件 作为 最外层
class Parent extends React.Component {
state = {
childContext: '123',
newContext: '456',
}
// react 静态方法 api
getChildContext() {
return { value: this.state.childContext, a: 'aaaaa' }
}
render() {
return (
<>
<div>
<label>childContext:</label>
<input
type="text"
value={this.state.childContext}
onChange={e => this.setState({ childContext: e.target.value })}
/>
</div>
<div>
<label>newContext:</label>
<input
type="text"
value={this.state.newContext}
onChange={e => this.setState({ newContext: e.target.value })}
/>
</div>
{/* 基于 Provider来传递 */}
<Provider value={this.state.newContext}>{this.props.children}</Provider>
</>
)
}
}
// 定义第二个父组件 作为 中间层
class Parent2 extends React.Component {
// { value: this.state.childContext, a: 'bbbbb' }
getChildContext() {
return { a: 'bbbbb' }
}
render() {
return this.props.children
}
}
// 定义第一个子组件 内部使用 Consumer
function Child1(props, context) {
console.log(context)
return <Consumer>{value => <p>newContext: {value}</p>}</Consumer>
}
// 声明子组件需要的 props
Child1.contextTypes = {
value: PropTypes.string,
}
// 定义第二个子组件
class Child2 extends React.Component {
render() {
return (
<p>
childContext: {this.context.value} {this.context.a}
</p>
)
}
}
// Child2.contextType = Consumer
// 声明 子组件2 需要的 props
Child2.contextTypes = {
value: PropTypes.string,
a: PropTypes.string,
}
// 父组件不声明,子组件无法获取 props
Parent.childContextTypes = {
value: PropTypes.string,
a: PropTypes.string,
}
// 父组件不声明,子组件无法获取 props
Parent2.childContextTypes = {
a: PropTypes.string,
}
// 最终的组件树,不同组件的嵌套
export default () => (
<Parent>
<Parent2>
<Child1 />
<Child2 />
</Parent2>
</Parent>
)
2 )关于 childContextTypes 旧版API的说明
主要以 Child2 组件来说明
上级组件中声明这个 getChildContext 这个方法
然后return的这个对象, 就是作为子组件当中能够获取这个 context 的对象
但是有一点必须要注意,就是父组件必须要声明 childContextTypes
, 即: Parent.childContextTypes
Parent.childContextTypes = {
value: PropTypes.string,
a: PropTypes.string,
}
上层组件是必须要声明的
如果不声明,它提供的这个 context ,子组件是无法获取到的
想要获取上层组件提供的 context,需要在子组件 Child2 当中
声明自己需要的 contextTypes, 例如
Child2.contextTypes = {
value: PropTypes.string,
a: PropTypes.string,
}
它的内容也是跟上层组件的 childContextTypes
是一样
两者区别是: 有或没有子组件
在这个渲染的过程中,比如这个 Child2 组件,希望获取父层组件中提供的 context 里面的某几个属性
Chid 自己就需要去声明使用几个属性,为何这么做呢
3 )关于 createContext 新版API的分析
react.createContext
,它返回了一个对象const { Provider, Consumer } = React.createContext('default')
Provider
和 Consumer
,是一个 context 提供方和 context 的订阅方<Provider value={this.state.newContext}>{this.props.children}</Provider>
Consumer
组件 (它传入的是一个回调方法)// 声明子组件需要的 props
Child1.contextTypes = {
value: PropTypes.string,
}
Provider
和 Consumer
是一一对应的关系4 )为什么要弃用老的API要改成这种新的API
定位到 reactContext.js
/**
* 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,
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> = {
$$typeof: REACT_CONTEXT_TYPE,
_calculateChangedBits: calculateChangedBits,
// As a workaround to support multiple concurrent renderers, we categorize
// some renderers as primary and others as secondary. 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.
_currentValue: defaultValue,
_currentValue2: defaultValue,
// These are circular
Provider: (null: any),
Consumer: (null: any),
};
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;
},
},
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 {
context.Consumer = context;
}
if (__DEV__) {
context._currentRenderer = null;
context._currentRenderer2 = null;
}
return context;
}
createContext
, 它接收的是一个 defaultValue 和 calculateChangeBits方法
$$typeof
, 这里的 $$typeof
跟 ReactElement 里面的 $$typeof
是不一样的 context.Provider = {
$$typeof: REACT_PROVIDER_TYPE,
_context: context,
};
context.Consumer = context
$$typeof
$$typeof
$$typeof
是完全没有任何关系的$$typeof
, 表明它是一个context的 Provider,或 Consumer