React Context(执行上下文)
一、Context
解决什么问题
React是单向数据流,数据是从上往下单向传递的,每个组件都可以接收父组件的属性和状态,也可以把属性和状态向下传递给子组件,但是当层级特别多的时候就会变得非常繁琐。Context 提供了一种在组件之间共享此类值得方式,而不必逐层传递。它主要是用来解决祖先组件向后代组件传递数据的问题(prop drilling
)。方便组件之间跨层级传递数据。
举个例子:用户登录之后,很多组件需要拿到用户相关信息,如果按照prop传递的方式获取,会变得异常繁琐,而且很难判断数据的真正来源。使用Context,就可以在Provider
的后代组件的任意位置,都可以Consumer
数据。
二、如何使用
- 1. 通过
createContext
创建Context - 2. 使用
Context.Provider
组件发布数据(通过给Context.Provider
传递value
属性)。 - 3. 如果是函数组件,那么所有后代组件,都可以通过
Context.Consumer
消费数据 - 4. 如果是类组件,那么所有的后代组件都需要通过 this.context.xxx 来获取
1.类组件的context
import React from "react";
import ReactDOM from "react-dom";
let ThemeContext = React.createContext();
class Content extends React.Component {
//取父组件传递过来得值
//先定义一个静态属性contextType 通过this.context.color拿到值
static contextType = ThemeContext
render() {
return <div style={{ border: `1px solid ${this.context.color}` }}>内容</div>;
}
}
class Main extends React.Component {
//取父组件传递过来得值
//先定义一个静态属性contextType 通过this.context.color拿到值
static contextType = ThemeContext
render() {
return (
<div style={{ border: `1px solid ${this.context.color}` }}>
子Main
<Content}></Content>
</div>
);
}
}
class Title extends React.Component {
//取父组件传递过来得值
//先定义一个静态属性contextType 通过this.context.color拿到值
static contextType = ThemeContext
render() {
return <div style={{ border: `1px solid ${this.context.color}` }}>标题</div>;
}
}
class Header extends React.Component {
//取父组件传递过来得值
//先定义一个静态属性contextType 通过this.context.color拿到值
static contextType = ThemeContext
render() {
return (
<div style={{ border: `1px solid ${this.context.color}` }}>
子Header
<Title></Title>
</div>
);
}
}
class Panel extends React.Component {
state = { color: "green" };
render() {
let colorvalue = { color: this.state.color };
// Provider 表示提供者,负责向下层所有得组件提供数据value 它得所有子组件都可以通过拿到value值
return (
<ThemeContext.Provider value={colorvalue}>
<div style={{ border: `1px solid ${this.state.color}`, width: `200px` }}>
Panel
<Header></Header>
<Main></Main>
</div>
</ThemeContext.Provider>
);
}
}
ReactDOM.render(<Panel></Panel>, document.getElementById("root"));
修改数据:
import React from "react";
import ReactDOM from "react-dom";
let ThemeContext = React.createContext();
class Content extends React.Component {
//取父组件传递过来得值
//先定义一个静态上下文类型contextType 通过this.context.value拿到值
static contextType = ThemeContext
render() {
return (
<div style={{ border: `1px solid ${this.context.color}` }}>
内容
<button onClick={() => this.context.changeColor('red')}>变红</button>
<button onClick={() => this.context.changeColor('green')}>变绿</button>
</div>
)
}
}
class Main extends React.Component {
//取父组件传递过来得值
//先定义一个静态上下文类型contextType 通过this.context.value拿到值
static contextType = ThemeContext
render() {
return (
<div style={{ border: `1px solid ${this.context.color}`}}>
子Main
<Content style={{ border: `1px solid ${this.context.color}` }}></Content>
</div>
);
}
}
class Title extends React.Component {
//取父组件传递过来得值
//先定义一个静态上下文类型contextType 通过this.context.value拿到值
static contextType = ThemeContext
render() {
return <div style={{ border: `1px solid ${this.context.color}` }}>标题</div>;
}
}
class Header extends React.Component {
//取父组件传递过来得值
//先定义一个静态上下文类型contextType 通过this.context.value拿到值
static contextType = ThemeContext
render() {
return (
<div style={{ border: `1px solid ${this.context.color}` }}>
子Header
<Title></Title>
</div>
);
}
}
class Panel extends React.Component {
state = { color: "green" };
changeColor = (color) => {
this.setState({color})
}
render() {
let colorvalue = { color: this.state.color, changeColor: this.changeColor };
// Provider 表示提供者,负责向下层所有得组件提供数据value 它得所有子组件都可以通过拿到value值
return (
<ThemeContext.Provider value={colorvalue}>
<div style={{ border: `1px solid ${this.state.color}`, width: `200px` }}>
Panel
<Header></Header>
<Main></Main>
</div>
</ThemeContext.Provider>
);
}
}
ReactDOM.render(<Panel></Panel>, document.getElementById("root"));
任何一个后代组件都可以修改数据,这会导致所有的后代组件都会获取到修改后的值,这显然是不可取的,最好还是由发布者修改。
2.函数组件的context
import React from "react";
import ReactDOM from "react-dom";
let ThemeContext = React.createContext();
function Content (props){
//Consumer 消费者 消费上下文中的value,通过value.xxx来获取发布的数据
return (
<ThemeContext.Consumer>
{
(value) => (
<div style={{ border: `1px solid ${value.color}` }}>
内容
<button onClick={() => value.changeColor('red')}>变红</button>
<button onClick={() => value.changeColor('green')}>变绿</button>
</div>
)
}
</ThemeContext.Consumer>
)
}
function Main (props){
//Consumer 消费者 消费上下文中的value,通过value.xxx来获取发布的数据
return (
<ThemeContext.Consumer>
{
(value) => (
<div style={{ border: `1px solid ${value.color}`}}>
子Main
<Content style={{ border: `1px solid ${value.color}` }}></Content>
</div>
)
}
</ThemeContext.Consumer>
)
}
function Title (props){
//Consumer 消费者 消费上下文中的value,通过value.xxx来获取发布的数据
return (
<ThemeContext.Consumer>
{
(value) => (
<div style={{ border: `1px solid ${value.color}` }}>标题</div>
)
}
</ThemeContext.Consumer>
)
}
function Header (props){
//Consumer 消费者 消费上下文中的value,通过value.xxx来获取发布的数据
return (
<ThemeContext.Consumer>
{
(value) => (
<div style={{ border: `1px solid ${value.color}` }}>
子Header
<Title></Title>
</div>
)
}
</ThemeContext.Consumer>
)
}
class Panel extends React.Component {
state = { color: "green" };
changeColor = (color) => {
this.setState({color})
}
render() {
let colorvalue = { color: this.state.color, changeColor: this.changeColor };
// Provider 表示提供者,负责向下层所有得组件提供数据value 它得所有子组件都可以通过拿到value值
return (
<ThemeContext.Provider value={colorvalue}>
<div style={{ border: `1px solid ${this.state.color}`, width: `200px` }}>
Panel
<Header></Header>
<Main></Main>
</div>
</ThemeContext.Provider>
);
}
}
ReactDOM.render(<Panel></Panel>, document.getElementById("root"));
可以看到函数组件需要通过context.Consumer来消费上下文中的value,并且通过value.xxx来获取发布的数据
三、context的底层实现原理
不积跬步无以至千里