React-Context2019-09-15 23:11

React-Context

主要的目的是跨组件通讯。有一些组件之间不是直接的父子关系。并且有一些情况下无法传递 props, 这时候可以考虑 context 来实现

有两种实现方式

  1. childContextType (V17 会废弃)
    • 使用 context 的下层组件全部都会重新渲染, 导致性能比较差
  2. createContext

childContextType (不推荐)

  1. 在父组件里面定义一个 getChildContext()
    getChildContext() {
      return { 
        value: this.state.childContext, a: 'aaaaa' 
      }
    }
    
  2. 定义子组件以及父组件的 context 的 propTypes
    • 子组件和父组件都需要定义
    • 没有定义的 attr 将会被忽略
      // 这里是对于对应的 context 的 attr 的声明
      Child.contextTypes = {
        value: PropTypes.string,
        a: PropTypes.string, 
        // 如果这里一个 attr 不声明, 那么就直接会显示为空
        // 比如, 如果没有声明 a, 那么 this.context.a 就无法获取到
      }
      
      // 在 Parent 里面也要进行对应的声明。
      Parent.childContextTypes = {
        value: PropTypes.string,
        a: PropTypes.string,
      }
      
  3. 在子组件里面通过 this.context 访问传递过来的 context
    • 如果一个子组件被多个父组件包含,那么他们里面的 context 会被 merge 然后最终传递给子组件。
      class Child extends React.Component {
        render() {
          return (
            <p>
              childContext: {this.context.value} {this.context.a}
            </p>
          )
        }
      }
      

详细例子: [childContextType 完整例子](#childContextType 完整例子)

流程图解释

流程如下图:

  1. 父组件定义了一个 getChildContext() 然后传 users 给 users
  2. 父组件和子组件的 propTypes 定义了 users 要求是一个 array
  3. 子组件接收到之后就可以访问了

createContext

  1. 首先通过 React.createContext() 生成一堆 tuple
    const { Provider, Consumer } = React.createContext('default')
    
    • 生成的 Provider 和 Consumer 也是一个 component, 并且要注意的是这两个是对应的。
  2. 父组件里面用 Provider 包裹一下 子组件
    <Provider value={this.state.newContext}>{this.props.children}</Provider>
    
  3. 子组件里面使用 Consumer, 并且这里需要传进去一个方法
    function Child1(props, context) {
      console.log(context)
      return <Consumer>{value => <p>newContext: {value}</p>}</Consumer>
    }
    

详细例子: [createContext 详细的代码示例](#createContext 详细的代码示例)

附录

childContextType 完整例子

import React from 'react'
import PropTypes from 'prop-types'


class Parent extends React.Component {
  state = {
    childContext: '123',
    newContext: '456',
  }

  getChildContext() {
    return { 
      value: this.state.childContext, a: 'aaaaa' 
    }
  }

  render() {
    return (
      <>
        <div>
          <label>childContext:</label>
          <input
            type="text"
            value={this.state.childContext}
            onChange={e => this.setState({ childContext: e.target.value })}
          />
        </div>
        {this.props.children}
      </>
    )
  }
}


// 在 child 里面直接访问就可以使用了。
class Child extends React.Component {
  render() {
    return (
      <p>
        childContext: {this.context.value} {this.context.a}
      </p>
    )
  }
}

// 另外也可以使用纯函数式组件, 多传一个参数即可
/*
function Child(props, context) {
  console.log(context)
  return <Consumer>{value => <p>newContext: {value}</p>}</Consumer>
}
*/

// 这里是对于对应的 context 的 attr 的声明
Child.contextTypes = {
  value: PropTypes.string,
  a: PropTypes.string, 
  // 如果这里一个 attr 不声明, 那么就直接会显示为空
  // 比如, 如果没有声明 a, 那么 this.context.a 就无法获取到
}

// 在 Parent 里面也要进行对应的声明。
Parent.childContextTypes = {
  value: PropTypes.string,
  a: PropTypes.string,
}

export default () => (
  <Parent>
    <span>
      <Child />
    </span>
  </Parent>
)

createContext 详细的代码示例

import React from 'react'
import PropTypes from 'prop-types'

// 通过 createContext 获得 context 的提供方和订阅方
const { Provider, Consumer } = React.createContext('default')

class Parent extends React.Component {
  state = {
    childContext: '123',
    newContext: '456',
  }


  render() {
    return (
      <>
        <div>
          <label>newContext:</label>
          <input
            type="text"
            value={this.state.newContext}
            onChange={e => this.setState({ newContext: e.target.value })}
          />
        </div>
        <Provider value={this.state.newContext}>{this.props.children}</Provider>
      </>
    )
  }
}

class Parent2 extends React.Component {
  render() {
    return this.props.children
  }
}

function Child1(props, context) {
  console.log(context)
  return <Consumer>{value => <p>newContext: {value}</p>}</Consumer>
}

export default () => (
  <Parent>
    <Parent2>
      <Child1 />
    </Parent2>
  </Parent>
)
Powered by Remix
|
Designed by szhshp
|
Copyright © szhshp 2022