Introduction
Creates a Context object. When React renders a component that subscribes to this Context object it will read the current context value from the closest matching Provider above it in the tree.
Usage
step 1:
const MyContext = React.createContext(defaultValue);
step 2:
Parent component: 规定好通过value属性添加要传递给子组件的属性值
class Page extends React.Component{
render () {
const contextValue = { propOne: "some test here" };
return (
<MyContext.Provider value={ contextValue }>
<Children />
</MyContext.Provider>
)
}
}
step 3:
Child component: 返回一个函数式组件,并将传递过来的value属性作为参数传入
class Child extends React.Component{
render () {
{
value => (<grandChild/>value.propOne</grandChild>)
}
}
}
Principle
method returns two class components: Provider & Consumer
function createContext (defaultValue) {
class Provider extends React.Component {
static value;
constructor(props) {
Provider.value = props.value;
this.state = { value: null };
}
getDerivedStateFromProps(nextProps, prevState) {
Provider.value = nextProps.value;
return { value: nextProps.value };
}
render () {
return this.props.children;
}
}
class Consumer extends React.Component {
render () {
return this.props.children(Provider.value);
}
}
return {
Provider,
Consumer,
}
}
这里给Provider添加类属性vaule,而不是给实例添加属性,是为了避免通过new来实例化组件才能拿到value值,节约性能。
栗子
import React from 'react';
import ReactDOM from 'react-dom';
// principle
function createContext() {
class Provider extends React.Component {
static value; // { changeColor: this.changeColor, color: this.state.color };
constructor(props) {
super(props);
Provider.value = props.value;
this.state = { value: props.value }
}
static getDerivedStateFromProps(nextProps, prevState) {
Provider.value = nextProps.value;
return { value: nextProps.value };
}
render() {
return this.props.children
}
}
class Consumer extends React.Component {
render() {
return this.props.children(Provider.value);
}
}
return {
Provider,
Consumer
}
}
const ThemeContext = createContext();
class Page extends React.Component{
constructor(props) {
super(props);
this.state = { color: '#d9534f', count: 0 };
}
changeColor = color => {
this.setState({ color });
};
add = () => this.setState(state => ({ count: state.count + 2 }));
minus = () => this.setState(state => (state.count ? { count: state.count -1 } : {count: state.count }));
render () {
let contextVal = { changeColor: this.changeColor, color: this.state.color, add: this.add, minus: this.minus };
return (
<ThemeContext.Provider value={ contextVal }>
<div style={ { width: 300, padding: 5, margin: '50px auto', border: `4px solid ${this.state.color}` } }>
<p> Count: { this.state.count }</p>
<Header />
<Main />
</div>
</ThemeContext.Provider>
)
}
}
class Header extends React.Component{
render() {
return (
<ThemeContext.Consumer>
{
(value) => (
<div style={{ border: `4px solid ${value.color}`, padding: 10 }}>
Header
<Title/>
</div>
)
}
</ThemeContext.Consumer>
)
}
}
class Title extends React.Component{
render() {
return (
<ThemeContext.Consumer>
{
value => (
<div style={{ border: `4px solid ${value.color}`, padding: 10 }}>Title</div>
)
}
</ThemeContext.Consumer>
)
}
}
class Main extends React.Component{
render() {
return (
<ThemeContext.Consumer>
{
value => (
<div style={{ border: `4px solid ${value.color}`, padding: 10, marginTop: 10 }}>
Main
<Content />
</div>
)
}
</ThemeContext.Consumer>
)
}
}
class Content extends React.Component{
render() {
return (
<ThemeContext.Consumer>
{
value => (
<div style={{ border: `4px solid ${value.color}`, padding: 10 }}>
Content
<br/>
<button className='btn btn-danger ml_10' onClick={() => value.changeColor("#d9534f")}>Pink</button>
<button className='btn btn-info ml_10' onClick={() => value.changeColor("#46b8da")}>Green</button>
<br/>
<button className='btn btn-secondary ml_10' onClick={ value.minus }>-</button>
<button className='btn btn-secondary ml_10' onClick={ value.add }>+</button>
</div>
)
}
</ThemeContext.Consumer>
)
}
}
ReactDOM.render(
<Page />,
document.getElementById('root')
);