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;