1.Context
Context 通过组件树提供了一个传递数据的方法,从而避免了在每一个层级手动的传递 props 属性。
有部分小伙伴应该使用props属性进行组件向下传值的操作。当多个组件嵌套时候。你就需要慢慢向上寻找最初的值是什么
2.API (个人大白话理解)
context api给出三个概念:React.createContext()、Provider、Consumer;
React.createContext:创建一个上下文的容器(组件), defaultValue可以设置共享的默认数据,并包含Provider、Consumer两个组件 、
import { createContext } from 'react'
const {Provider, Consumer} = React.createContext(defaultValue);
Provider(生产者): 和他的名字一样。用于生产共享数据的地方。生产什么呢? 那就看value定义的是什么了。value:放置共享的数据。
<Provider value={/*共享的数据*/}>
/*里面可以渲染对应的内容*/
</Provider>
Consumer(消费者):这个可以理解为消费者。 他是专门消费供应商(Provider 上面提到的)产生数据。Consumer需要嵌套在生产者下面。才能通过回调的方式拿到共享的数据源。当然也可以单独使用,那就只能消费到上文提到的defaultValue
<Consumer>
{value => /*根据上下文 进行渲染相应内容*/}
</Consumer>
每当Provider的值发生改变时, 作为Provider后代的所有Consumers都会重新渲染
下面是一个例子
自定义context
import { createContext } from 'react'
// 自定义context
const ColorContext = createContext({color: 'green'})
// Provider提供器组件, Consumer消费者组件
const { Provider, Consumer } = ColorContext
export default ColorContext
export {
Provider,
Consumer
}
在顶级组件定义公共状态
import React, { Component } from 'react'
import Child1 from './Child1'
import Child2 from './Child2'
import { Provider } from './ColorContext'
class Index extends Component {
render() {
return (
// 在一个祖先组件定义公共状态
<Provider value={{color="red"}}>
<div>
<h1>Parent</h1>
<Child1></Child1>
<Child2></Child2>
</div>
</Provider>
);
}
}
export default Index;
在子组件上使用
Child1.jsx
import React, { Component } from 'react'
import GrandSon1 from './GrandSon1'
import ColorContext from './colorContext'
class Child1 extends Component {
// contextType静态属性,用来定义context
// this组件实例上的context属性就有值了
static contextType = ColorContext
componentDidMount() {
// console.log(this)
}
render() {
return (
<div>
<h2 style={{color: this.context.color}}>child1</h2>
<GrandSon1></GrandSon1>
</div>
);
}
}
export default Child1;
孙子组件也能拿到
GrandSon1.jsx
import React, { Component } from 'react'
import ColorContext from './colorContext';
class GrandSon1 extends Component {
static contextType = ColorContext
render() {
return (
<div style={{color: this.context.color}}>
grandson1
</div>
);
}
}
export default GrandSon1;
使用消费者拿到上文的值
child2,jsx
import React, { Component } from 'react'
import GrandSon2 from './GrandSon2'
import GrandSon3 from './GrandSon3'
import { Consumer } from './colorContext'
class Child2 extends Component {
renderTitle = () => {
return <h1>my title</h1>
}
render() {
return (
<div>
<Consumer>
{
value => {
return (
<h2 style={{color: value.color}}>child2</h2>
)
}
}
</Consumer>
{/* render props 在调用Consumer这个组件的时候,Provider会把value的值传给Consumer渲染函数里的value,就类似于下面 GrandSon2里的渲染函数里的str,GrandSon2组件可以传值给它*/}
<GrandSon2 title={this.renderTitle}>
{
(str) => {
return <h2>child content {str}</h2>
}
}
</GrandSon2>
</div>
);
}
}
export default Child2;
GrandSon2.jsx
import React from 'react'
import ColorContext, { Consumer } from './colorContext'
export default function GrandSon2(props) {
return (
<div>
<span>
{props.children('!!!')}
</span>
</div>
)
}
基于上面的例子,我们可以自己封装一个Provider
import React, { createContext, Component } from 'react'
const colorContext = createContext()
const { Provider, Consumer: ColorConsumer } = colorContext
class ColorProvider extends Component {
state = {
color: this.props.color
}
changeColor = color => {
return () => {
this.setState({
color
})
}
}
render() {
return (
<Provider value={{
color: this.state.color,
changeColor: this.changeColor
}}>
{this.props.children}
</Provider>
)
}
}
export {
ColorProvider,
ColorConsumer
}
顶层组件
index.jsx
import React, { Component } from 'react'
import Child2 from './Child2'
import { ColorProvider } from './ColorContextComp'
class Index extends Component {
render() {
return (
// 在一个祖先组件定义公共状态
<ColorProvider color="red">
<div>
<h1>Parent</h1>
<Child3></Child3>
</div>
</ColorProvider>
);
}
}
export default Index;
Child3.jsx
import React, { Component } from 'react'
import { ColorConsumer } from './ColorContextComp'
class Child3 extends Component {
render() {
return (
<ColorConsumer>
{
value => {
return (
<div style={{color: value.color}}>
hello
<button onClick={value.changeColor('purple')}>change color</button>
</div>
)
}
}
</ColorConsumer>
);
}
}
export default Child3;