什么是Context?
Context被翻译为上下文,在编程领域,这是一个经常会接触到的概念,React中也有。
在React的官方文档中,被归类为高级部分(Advanced),属于React的高级API,但官方并不建议在稳定版
的App中使用Context。
为什么要使用Context?
当你的应用组件层级非常多的时候,有时候需要把props一层一层传递,尤其是把props从顶层传递到最底层,而中间的组件又不需要这个props的时候,React Context就显得尤其重要了。
如上图所示的情况下,需要将组件APP的值传递到最后一个组件Node,但是中间的组件又不需要这个props,这样就造成了没必要的渲染和性能的损耗。
React Context可以来处理这种混乱的场景,不需要逐层传递,达到
按需消费的目的。
Context的使用方法(三步走)源码
1、创建Context对象
Context.js
import React from "react";
export const Context = React.createContext();
2、创建生产者Provider,传递value
ContextPage.js
import React,{Component} from "react";
import { Context } from "../Contex";
import ContextTypePage from "./ContextTypePage";
export default class ContextPage extends Component {
constructor(props){
super(props);
this.state = {
theme:{
themeColor:"red"
}
}
}
render(){
const {theme} = this.state;
return (
<div>
<Context.Provider value={theme}>
<ContextTypePage/>
</Context.Provider>
</div>
)
}
}
3、子组件消费者使用value
3.1 第一种方式ContextType
ContextTypePage.js
import React,{Component} from "react";
import { Context,UserContext } from "../Contex";
export default class ContextTypePage extends Component {
static contextType = Context;
render(){
const {themeColor} = this.context;
return (
<h3 className={themeColor}>
ContextTypePage
</h3>
)
}
}
执行结果:
3.2 第二种方式useContext
3.2.1 创建useContext
export const UserContext = React.createContext();
3.2.2 创建生产者UserContext.Provider
<UserContext.Provider value={user}>
<ContextTypePage/>
<UseContextPage/>
</UserContext.Provider>
3.2.2 创建消费者UseContextPage
import React from "react";
import { Context,UserContext } from "../Contex";
export default function UseContextPage(props) {
const user = React.useContext(UserContext);
return (
<div>
<h3 className={theme.themeColor}>UseContextPage </h3>
</div>
);
}
执行结果:
3.3 第三种方式Consumer
3.3.1 创建消费者
import React,{Component} from "react";
import { Context,UserContext } from "../Contex";
export default class ConsumerPage extends Component{
render(){
return (
<div>
<h3>ConsumerPage</h3>
<Context.Consumer>
{
theme => {
return (
<div className={theme.themeColor}>omg
<UserContext.Consumer>
{
user=><p>{user.name}</p>
}
</UserContext.Consumer>
</div>
)
}
}
</Context.Consumer>
</div>
)
}
}
3.3.2 引入消费者
<Context.Provider value={theme}>
<UserContext.Provider value={user}>
<ConsumerPage/>
</UserContext.Provider>
</Context.Provider>
Context三种方法的比较
ContextType
挂载在 class 上的 contextType 属性会被重赋值为⼀个由React.createContext() 创建的 Context对象。这能让你使⽤ this.context 来消费最近 Context 上的那个值。你可以在任何⽣命周期中访问到它,包括 render 函数中。
- 只能用在类组件中
- 只能订阅单一的context来源,有多个的时候,后者会覆盖前者。
useContext
接收⼀个 context 对象( React.createContext 的返回值)并返回该 context 的当前值。当前的 context 值由上层组件中距离当前组件最近的 <MyContext.Provider> 的 value prop 决定。只能⽤ 在function组件中。
- 只能在函数组件,自定义hook组件中使用
- 可以订阅多个context来源
Consumer
React 组件也可以订阅到 context 变更。这能让你在函数式组件中完成订阅 context。 这个函数接收当前的 context 值,返回⼀个 React 节点。传递给函数的 value 值等同于往上组件树离 这个 context 最近的 Provider 提供的 value 值。如果没有对应的 Provider, value 参数等同于传递 给 createContext() 的 defaultValue。
- 不限制组件的类型
注意事项:
context 会使⽤参考标识(reference identity)来决定何时进⾏渲染,这⾥可能会有⼀些陷阱,当 provider 的⽗组件进⾏重渲染时,可能会在 consumers 组件中触发意外的渲染。举个例⼦,当每⼀次 Provider 重渲染时,以下的代码会重渲染所有下⾯的 consumers 组件,因为 value 属性总是被赋值 为新的对象:
class App extends React.Component {
render() {
return (
<Provider value={{ something: 'something' }}>
<Toolbar />
</Provider>
);
}
}
为了防⽌这种情况,将 value 状态提升到⽗节点的 state ⾥:
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
value: { something: 'something' },
};
}
render() {
return (
//FormPage.js
class
<Provider value = {this.state.value
}>
<Toolbar />
</Provider >
);
}
}
总结
在React的官⽅⽂档中, Context 被归类为⾼级部分(Advanced),属于React的⾼级API,建议不要滥 ⽤。