Context是16.3.0正式确定的API,在这之前Context是以实验性API的身份存在。 本文没有关于16.3.0之前版本中Context相关API的解释
核心逻辑:发布订阅模式
核心概念:Context(执行上下文)、Provider(发布者)、Consumer(订阅者)
Context 解决什么问题
Context 设计用来解决祖先组件向后代组件传递数据的问题(prop drilling
)。为跨层级的组件搭建一座桥梁。
举个例子:用户登录之后,很多组件需要拿到用户相关信息,如果按照prop传递的方式获取,会变得异常繁琐,而且很难判断数据的真正来源
而如果使用Context,则在Provider
的后代组件的任意位置,都可以Consumer
数据
有同学可能要问,使用`redux`不也可以解决这个问题吗?答案是:你说的对。不过redux本身就是通过context实现的。
如何使用 Context
步骤 1. 通过createContext
创建Context 2. 使用Context.Provider
组件发布数据(通过给Context.Provider
传递value
属性)。 3. Context.Provider
的所有后代组件,都可以通过Context.Consumer
消费数据
Demo: 创建一个用于存放用户数据的Context
// 从react库中引入createContext方法
import React, { Component, createContext } from 'react'
// 通过createContext方法创建用于存放用户数据的Context类
const UserContext = createContext()
// 通过UserContext的属性Provider(组件)发布用户数据
// 传递给Provider的value属性的值可以是任意数据类型,甚至是函数
class App extends Component {
state = {
user: {
name: 'abc',
},
}
render() {
const {
user,
} = this.state
return (
<UserContext.Provider value={user}>
<UseUserInfoComponent />
</UserContext.Provider>
)
}
}
// 通过UserContext的属性Consumer(组件)消费用户数据
// Consumer的子组件必须是一个function,通过function的参数接收顶层传入的数据
class UseUserInfoComponent extends Component {
render() {
return (
<UserContext.Consumer>
{
context => (
<div>
<p>{context.name}</p>
</div>
)
}
</UserContext.Consumer>
)
}
}
复制代码
使用中需要注意的点
- 调用
creatContext
时可以传入默认值,但该值不是Provider的默认值,而是作为Consumer的默认值,仅当没有Provider的时候才生效
const UserContext = createContext({
user: {
name: 'max',
},
})
class App extends Component {
render() {
return (
{/* 此时Provider没有传入value属性,默认会将value设置为undefined */}
<UserContext.Provider>
<UseUserInfoComponent />
</UserContext.Provider>
)
}
}
class UseUserInfoComponent extends Component {
render() {
return (
<UserContext.Consumer>
{
// 这里是context的值为undefined,因为Provider没有传value
context => (
<div>
<p>{context.name}</p> {/* 报错 */}
</div>
)
}
</UserContext.Consumer>
)
}
}
复制代码
正确的使用createContext传入的默认值的用法是不提供Provider
const UserContext = createContext({
user: {
name: 'max',
},
})
class App extends Component {
render() {
return (
<UseUserInfoComponent />
)
}
}
class UseUserInfoComponent extends Component {
render() {
return (
<UserContext.Consumer>
{
context => (
<div>
<p>{context.name}</p> {/* 可以获取到默认值 */}
</div>
)
}
</UserContext.Consumer>
)
}
}
复制代码
- 任何订阅者(Consumer)都可以直接修改context,这会导致后续的订阅者获取到修改后的context值,但这显然是不可取的。如果需要修改,应该统一由发布者(Provider)修改。
未完待续。。。