使用Context来解决跨组件通信
React.createContext
- React.createContext()
- Consumer
- Provider
注意项
- Provider 组件上的属性,要传递的内容都写成它的属性
<Provider value={{
counter: this.state.counter,
incrementCounter: this.incrementCounter,
decrementCounter: this.decrementCounter
}}>
{ this.props.children }
</Provider>
- Consumer 组件包裹的里面是一个函数,用 return 返回要显示的内容
- 参数就是 Provider 属性名和值得对象结构
<CounterConsumer>
{
(arg) => {
// arg 是一个对象 里面装的是Provide组件的属性名和值的对象结构
// {counter: 100, incrementCounter: ƒ, decrementCounter: ƒ}
return (
<span>{arg.counter}</span>
)
}
}
</CounterConsumer>
- React.createContext() 我们结构的时候通常会给Consumer起一个别名
个人理解
-
这样做解决了什么问题:Provide,Consumer用来解决夸多层组件传值问题,如果我在Counter 组件里面加其他组件也是可以直接通过 arg 来访问 Provider 上传过来的值得
-
方便记忆:这个有点像vue 中的 provide/inject ,父组件provide数据,后代组件里面要用到时就用 inject 注入接收
- 最外层套一个 Provider 组件,
- App 组件上没有加属性,跨过这个组件传递数据到下一级
- 子组件 用 Consumer 包裹接收
demo
import React, { Component, Fragment, createContext } from 'react'
import { render } from 'react-dom'
const {
Provider,
Consumer: CounterConsumer
} = createContext()
class CounterProvide extends Component {
constructor() {
super()
this.state = {
counter: 100
}
}
incrementCounter = () => {
this.setState({
counter: this.state.counter + 1
})
}
decrementCounter = () => {
this.setState({
counter: this.state.counter - 1
})
}
render() {
return (
<Provider value={{
counter: this.state.counter,
incrementCounter: this.incrementCounter,
decrementCounter: this.decrementCounter
}}>
{
// 老是忘记写这句
// CounterProvide 组件子组件都显示在这个里面
this.props.children
}
</Provider>
)
}
}
class CountBtn extends Component {
render() {
return (
<CounterConsumer>
{arg => {
return (
<button onClick={
this.props.type === "increment" ? arg.incrementCounter : arg.decrementCounter
}>
{this.props.children}
</button>
)
}}
</CounterConsumer>
)
}
}
class Counter extends Component {
render() {
return (
<CounterConsumer>
{
(arg) => {
return (
<span>{arg.counter}</span>
)
}
}
</CounterConsumer>
)
}
}
class App extends Component {
render() {
return (
<Fragment>
<CountBtn type="increment">+</CountBtn>
<Counter></Counter>
<CountBtn type="decrement">-</CountBtn>
</Fragment>
)
}
}
render(
<CounterProvide>
<App />
</CounterProvide>,
document.querySelector('#root')
)