React Context(上下文)
文章目录
一.初识Context
Context 提供了一个无需为每层组件手动添加 props,就能在组件树间进行数据传递的方法.
二.作用
Context 设计目的是为了共享那些对于一个组件树而言是“全局”的数据,例如当前认证的用户、主题或首选语言
context可以进行跨级组件通信.其原理是生产-消费者模式
三.使用Context
1.React.createContext
创建一个上下文容器组件
这个组件会从组件树中离自身最近的那个匹配的Provider中,读取到当前的context值
//定义一个全局的context
const GlobalContext = React.createContext(defaultValue);
PS:defaultValue是默认的数据,当组件所处的树中没有匹配到 Provider 时,其 defaultValue
参数才会生效,将 undefined
传递给 Provider 的 value 时,消费组件的 defaultValue
不会生效。
2.Provider
生产者,用于生产共享数据的地方, 在value中放置需要共享的数据
provider接收value属性,传递给消费者,一个provider对应多个consumer消费者,当provider的value值发生变化,所有的消费者组件,都会重新渲染.
class Son extends React.Component {
render() {
return (
//提供一个生产者
<GlobalContext.Provider value={
{
//传递给消费者的数据
msg: this.state.msg,
//传递给消费者,一个函数,提供改变生产者内部的一个参数
onChangeShow: (text) => {
this.setState({
msg: text
});
},
}
}>
<div>
<h1>我是父组件</h1>
</div>
</GlobalContext.Provider>
);
}
3.Consumer
消费者,专门消费生产者提供的数据
接收当前的context值,返回一个React节点,获取到的value值是往上组件树离这个context最近的生产者提供的value,如果没有生产者,会直接读取createContext()
对象的默认值defaultValue
class Son extends React.Component {
render() {
return (
<GlobalContext.Consumer>
{
context => (
<div>
<h1>我是儿子组件,我要消费父亲的数据</h1>
</div>
)
}
</GlobalContext.Consumer>
);
}
}
4.Class.contextType
需要消费生产者提供的数据,还有一种方法,也是可以的,设置组件的属性为contextType
,值为React.createContext()
创建的上下文context对象,可以直接通过this.context
来访问最近的Context上的值,任何生命周期都可以访问
class Hello extends React.Component {
render() {
console.log(this.context);
return (
<div>
</div>
);
}
}
Hello.contextType = GlobalContext;
5.Context.displayName
可以通过给当前的上下文对象设置一个名字,便于在DevToolszhong中查看调试
const GlobalContext = React.createContext();
GlobalContext.displayName = 'MyDisplayName';
<GlobalContext.Provider> // "MyDisplayName.Provider" 在 DevTools 中
<GlobalContext.Consumer> // "MyDisplayName.Consumer" 在 DevTools 中
6.注意事项
因为 context 会使用参考标识(reference identity)来决定何时进行渲染,这里可能会有一些陷阱,当 provider 的父组件进行重渲染时,可能会在 consumers 组件中触发意外的渲染。举个例子,当每一次 Provider 重渲染时,以下的代码会重渲染所有下面的 consumers 组件,因为
value
属性总是被赋值为新的对象:
class App extends React.Component {
render() {
return (
<MyContext.Provider value={{something: 'something'}}>
<Toolbar />
</MyContext.Provider>
);
}
}
为了防止这种情况,将 value 状态提升到父节点的 state 里:
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
value: {something: 'something'}, };
}
render() {
return (
<Provider value={this.state.value}> <Toolbar />
</Provider>
);
}
}
所以,在Provider中定义的属性value,最好是在state先声明,之后再赋值给当前的value属性
四,应用场景
1.主题切换
export const themes = {
light: {
foreground: '#000000',
background: '#eeeeee',
},
dark: {
foreground: '#ffffff',
background: '#222222',
},
};
export const ThemeContext = React.createContext(
themes.dark // 默认值
);
2.获取当前认证的用户
3.React-router使用context
4.React-redux使用contxt
5.跨层级组件传参
context使用场景还有很多,不一一列举了!
五.缺点
context主要场景在于很多不同层级的组件需要访问同样的一些数据,但是不建议使用,因为当结构复杂时,这种全局变量不易追溯到源头,不知道从哪里传递过来的,会导致应用混乱,难以维护,更增强了程序的复杂性
如果只是为了避免层层传递属性,可以使用组件组合,可能效果会是比context更好的解决方案