11-react-context上下文
上下文
上下文(Context) 提供了一种通过组件树传递数据的方法,无需在每个级别手动传递 props 属性。非常之方便。
在典型的 React 应用程序中,数据通过 props 自上而下(父到子)传递,但对于应用程序中许多组件所需的某些类型的 props(例如环境偏好,UI主题),这可能很麻烦。 上下文(Context) 提供了在组件之间共享这些值的方法,而不必在树的每个层级显式传递一个 prop 。
何时用Context?
顶层数据改变,让下面所有的都改变,例如,点击按钮,使子组件的主题都进行交换。注意:不使用props传值。
在源码中,容器提供了proviser consumer,创建两个组件,且可以传入默认值以备用。
正文:
定义两个主题:
const themes = {
light: {
color: "pink",
background: "pink"
},
dark: {
color: 'white',
background: "white"
}
}
然后创建一个 { Provider, Consumer }
对。当 React 渲染 context Consumer
时,它将从组件树中匹配最接近的 Provider
中读取当前的 context 值。
defaultValue
参数 仅 当 Consumer(使用者) 在树中没有匹配的 Provider(提供则) 时使用它。这有助于在不封装它们的情况下对组件进行测试。注意:将 undefined
作为 Provider(提供者) 值传递不会导致 Consumer(使用者) 使用 defaultValue
。
在这里,我们定义默认为粉色主题:
const { Provider, Consumer } = React.createContext({ theme: themes.light })
先去定义一个根组件,在根组件中嵌套一个A作为子组件,B也作为子组件,但不同的是,一个会用于provider的使用,另一个不会。
render(){
return(
<A>A组件</A>
<ThemeButton />
<B/>
)
}
function ThemeButton() {
return (
<button>change theme<button/>
)
当点击按钮时,进行主题切换。
方法,用到一个三元:
handleTheme = () => {
const theme = this.state.theme === themes.light ? themes.dark : themes.light
this.setState({
theme
})
}
定义好方法,就可以在provider中设置value了。
(注意为什么在一个构造函数中,是因为在该构造函数中的方法,会等到非构造函数中的方法挂载完再运行,这样就避免了里面的方法是undefined的现象。)
constructor() {
super();
// 在这里能拿到箭头函数,在外面拿不到
this.state = { theme: themes.light, handleTheme: this.handleTheme }
}
下面,回到根组件中的provider中,传入value给consumer。
render() {
return (
<div>
<Provider value={this.state}>
<A >A 组件,我被包裹了</A>
<ThemeButton />
</Provider>
<B />
</div>
)
}
在上述代码中,包裹在provider中的组件才会被掌控,组件B的主题始终都会是默认值。
已经将值传给子组件,下面用Consumer来接收并使用:
通过一个回调的方式拿到对象,对象里包含balue传过来的所有数据,有主题和方法,可以拆解出来的。 另外要注意consumer只能是后面接{}的格式,不可以中间隔div,亲测报错。来到A组件中:
function A() {
return (
<Consumer>
{
//拿到这个 通过函数传过来 值
(obj) => {
console.log(obj);
const {theme,handleTheme} = obj
return (
<div>
<h1 style={{ ...theme }}>A Component,我被包裹了</h1>
</div>
)
}
}
</Consumer>
)
}
再来到按钮组件中,同样的方法,回调解构取值
function ThemeButton() {
return (
<Consumer>{
(obj) => {
const {theme,handleTheme} = obj
return (
<button onClick={handleTheme
} style={{ ...theme }} >change theme</button>
)
}
}
</Consumer>
)
}
真的很方便吧。
多个context
为了保持 context 的快速重新渲染,React 需要使每个 context Consumer 成为树中的一个独立节点。
设置provider:
const MoneyContext = React.createContext(0)
const HouseContext = React.createContext("")
class F extends React.Component {
state = {
money: 50,
house: "apartment"
}
render() {
return (
// 区分provider
<MoneyContext.Provider value={this.state.money}>
<HouseContext.Provider value={this.state.house}>
<S />
</HouseContext.Provider>
</MoneyContext.Provider>
)
}
}
使用consumer:
return (
<MoneyContext.Consumer>
{
(money) => {
return (
<HouseContext.Consumer>
{
(house) => {
return (
<div>
<p>{money}</p>
<p>{house}</p>
</div>
)
}
}
</HouseContext.Consumer>
)
}
}
</MoneyContext.Consumer>
)
就完成数据的展示了!yeah.