React-Context 提供了一个无需为每层组件手动添加 props,就能在组件树间进行数据传递的方法。

context

Context 提供了一个无需为每层组件手动添加 props,就能在组件树间进行数据传递的方法。

在一个典型的 React 应用中,数据是通过 props 属性自上而下(由父及子)进行传递的,但这种做法对于某些类型的属性而言是极其繁琐的(例如:地区偏好,UI 主题),这些属性是应用程序中许多组件都需要的。Context 提供了一种在组件之间共享此类值的方式,而不必显式地通过组件树的逐层传递 props。

优缺点

优点

跨组件访问数据

缺点

react组件树种某个上级组件shouldComponetUpdate返回false,当 context更新时,不会引起下级组件更新

React.createContext

const MyContext = React.createContext(defaultValue);

创建一个 Context 对象。当 React 渲染一个订阅了这个 Context 对象的组件,这个组件会从组件树中离自身最近的那个匹配的 Provider 中读取到当前的 context 值。

只有当组件所处的树中没有匹配到 Provider 时,其 defaultValue 参数才会生效。这有助于在不使用 Provider 包装组件的情况下对组件进行测试。注意:将 undefined 传递给 Provider 的 value 时,消费组件的 defaultValue 不会生效。

Context.Provider

<MyContext.Provider value={/* 某个值 */}>

每个 Context 对象都会返回一个 Provider React 组件,它允许消费组件订阅 context 的变化。

Provider 接收一个 value 属性,传递给消费组件。一个 Provider 可以和多个消费组件有对应关系。多个 Provider 也可以嵌套使用,里层的会覆盖外层的数据。

当 Provider 的 value 值发生变化时,它内部的所有消费组件都会重新渲染。Provider 及其内部 consumer 组件都不受制于 shouldComponentUpdate 函数,因此当 consumer 组件在其祖先组件退出更新的情况下也能更新。

通过新旧值检测来确定变化,使用了与 Object.is 相同的算法。

Context.Consumer

注意:Context.Consumer内必须是回调函数,通过context方法改变根组件状态

<MyContext.Consumer>
  {value => /* 基于 context 值进行渲染*/}
</MyContext.Consumer>

这里,React 组件也可以订阅到 context 变更。这能让你在函数式组件中完成订阅 context。

这需要函数作为子元素(function as a child)这种做法。这个函数接收当前的 context 值,返回一个 React 节点。传递给函数的 value 值等同于往上组件树离这个 context 最近的 Provider 提供的 value 值。如果没有对应的 Provider,value 参数等同于传递给 createContext()defaultValue

实例

import React,{Component} from 'react'
const myContext = React.createContext()

class Child extends Component{
  UNSAFE_componentWillUpdate (){
    console.log('componentWillUpdate1');
  }
  componentDidUpdate () {
    console.log('componentDidUpdate1');
  }
  render () {
    return (
      <myContext.Consumer>
        {
          context=>(
            <div>
              child -- {context.myname} -- {context.age}  -- {context.sex}
              <button onClick={this.handleClick.bind(this,context)}>change</button>
            </div>
          )
        }
      </myContext.Consumer>
    )
  }
  handleClick = (context)=>{
    context.changeState('xiaoming')
  }
}

export default class App extends Component {
  state={
    myname:'retr0'
  }
  UNSAFE_componentWillUpdate (){
    console.log('componentWillUpdate');
  }
  componentDidUpdate () {
    console.log('componentDidUpdate');
  }
  shouldComponentUpdate (nextProps,nextState) {
    return !(this.state.myname===nextState.myname)
  }
  changeState = (data)=>{
    this.setState({
      myname:data
    })
  }
  render () {
    return (
      <myContext.Provider value={{
        myname:this.state.myname,
        age:12,
        sex:1,
        changeState:this.changeState
      }}>
         <div>
          app
          <Child></Child>
        </div>
      </myContext.Provider>
    )
  }
}