React新特性-Context(一)

Context提供了一种方式,能够让数据在组件树种传递而不必使用Props一级一级手动传递

系列目录

何时使用 Context

例如:

export default class Page extends Component {
  constructor(props){
    super(props)
    this.state={
      theme:'red'
    }
  }
  onChangeTheme =(color)=>{
    this.setState({theme:color})
  }
  render() {
    const data = [{
      id: 1,
      text: '随随便便输入',
      color: 'yellow'
    }, {
      id: 2,
      text: '随便输入',
      color: 'blue'
    }]
    return (
      <div>
        <List data={data} theme={this.state.theme} onChangeTheme={this.onChangeTheme} />
      </div>
    )
  }
}

class List extends Component {
  // List 组件接受一个额外的“theme,onChangeTheme”属性,然后传递给 Item 组件。
  // 如果应用中每一个单独的按钮都需要知道 theme 的值,这会是件很麻烦的事,
  // 因为必须将这个值层层传递所有组件。
  render() {
    return (
      <div>
        {
          this.props.data.map(i => (
            <Item
              key={i.key}
              theme={this.props.theme}
              onChangeTheme={this.props.onChangeTheme}
              color={i.color}>
                {i.text}
            </Item>
          ))
        }
      </div>
    )
  }
}

class Item extends Component {
  render() {
    return (
      <div>
        <p style={{ color: this.props.theme }}>
          {this.props.children} <button onClick={() => this.props.onChangeTheme(this.props.color)}>点击变色</button>
        </p>
      </div>
    )
  }
}

上面的例子theme不仅仅层层传递,可能中间层List根本就用不到theme,但是每次还是要获取theme进行传递下去

下面来看看使用Context如何解决theme属性和onChangeTheme事件层层传递的问题

// Context 可以让我们无须明确地传遍每一个组件,就能将值深入传递进组件树。
const ThemeContext = React.createContext()

export default class Page extends Component {
  constructor(props){
    super(props)
    this.state={
      theme:'red'
    }
  }
  onChangeTheme =(color)=>{
    this.setState({theme:color})
  }
  render() {
    const data = [{
      id: 1,
      text: '随随便便输入',
      color: 'yellow'
    }, {
      id: 2,
      text: '随便输入',
      color: 'blue'
    }]
    // 使用一个 Provider 来将当前的 theme 传递给以下的组件树。
    // 无论多深,任何组件都能读取这个value值。
    return (
      <div>
        <ThemeContext.Provider value={{
          theme:this.state.theme,
          onChangeTheme:this.onChangeTheme
        }}>
          <List data={data} />
        </ThemeContext.Provider >
      </div>
    )
  }
}
//这里就不需要通过props传递theme和onChangeTheme
class List extends Component {
  render() {
    return (
      <div>
        {this.props.data.map(i => (<Item key={i.id} color={i.color}>{i.text}</Item>))}
      </div>
    )
  }
}

class Item extends Component {
  // 指定 contextType 读取当前的 theme context。
  // React 会往上找到最近的 theme Provider,然后使用它的值。
  static contextType = ThemeContext;

  render() {
    return (
      <div>
        <p style={{ color: this.context.theme }}>
          {this.props.children}
          <button onClick={() => this.context.onChangeTheme(this.props.color)}>
            点击变色
          </button>
        </p>
        <ThemeContext.Consumer>
          {
            value=>{
              console.log(value,'render')
            }
          }
        </ThemeContext.Consumer>
      </div>
    )
  }
}

Context API

React.createContext

const MyContext = React.createContext(defaultValue);

创建一个Context实例对象,同时这个对象派生出两个React组件,一个是<Context.Provider>,另一个是<Context.Consumer>

Context.Provider(生产者组件)

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

Provider接收一个 value 属性,传递给所有子级组件(消费者组件)。一个 Provider 可以和多个子集组件有对应关系。多个 Provider 也可以嵌套使用,里层的会覆盖外层的数据。
Providervalue 值发生变化时,它内部的所有子集组件都会重新渲染。

Context.Consumer(消费者组件)

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

消费者组件,用来接收Provider提供的变量value值,这里Consumer不能直接拿到value值,必须要以一个函数来拿出value

Class.contextType

class MyClass extends React.Component {
  componentDidMount() {
    let value = this.context;
    /* 在组件挂载完成后,使用 MyContext 组件的值来执行一些有副作用的操作 */
  }
  componentDidUpdate() {
    let value = this.context;
    /* ... */
  }
  componentWillUnmount() {
    let value = this.context;
    /* ... */
  }
  render() {
    let value = this.context;
    /* 基于 MyContext 组件的值进行渲染 */
  }
}
//挂载到class身上
MyClass.contextType = MyContext;

挂载在 class 上的 contextType 属性会被重赋值为一个由 React.createContext() 创建的 Context 对象。这能让你使用 this.context 来消费最近 Context 上的那个值。你可以在任何生命周期中访问到它,包括 render 函数中。

总结

使用React.createContext创建Context对象,然后利用<Context.Provider>组件包裹子级组件传递给所有子级组件,子集组件不能直接拿到Provider中的value值,所以使用<Context.Consumer>或者contextType这两种方式去拿到Provider中的value

Context 主要应用场景在于很多不同层级的组件需要访问同样一些的数据。请谨慎使用,因为这会使得组件的复用性变差。


文章作者: jackie chen
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 jackie chen !
评论
 上一篇
React新特性-Lazy和Suspense(二) React新特性-Lazy和Suspense(二)
系列目录 React新特性-Context(一) React新特性-Lazy和Suspense(二) React新特性-memo(三) React新特性-Hooks之useState(四) React新特性-Hooks之useEffect和
2019-07-09
下一篇 
git常用命令 git常用命令
git config --global user.name “你的名字” 让你全部的Git仓库绑定你的名字git config --global user.email “你的邮箱” 让你全部的Git仓库绑定你的邮箱git init 初始化你
2019-07-06
  目录