React中的Context和Portals用法

React16.3更新了不少新的内容:生命周期、Context、React.createRef()、Portals等等。对于更新飞快的前端来讲,咱们应该已经习惯了要不断学习╮(╯▽╰)╭。本文将介绍官方文档两个结合新内容Context和Portals。前端

Context

Context 提供了一种不用手动一层层传递props就能在组件树中传递数据的方式。react

在传统的React应用中,数据一般从父组件经过props一层层传递给子组件,可是这种方式在属性须要被不少组件用到的时候会显得很麻烦。数组

因此Context就被设计来在组件树中共享一些“全局”数据。app

直接看例子吧dom

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

class App extends Component {
  render () {
    return (
      <ThemeContext.Provider value="dark">
        <Toolbar />
      </ThemeContext.Provider>
    )
  }
}

function Toolbar () {
  return (
    <div>
      <ThemeButton />
    </div>
  )
}

function ThemeButton (props) {
  return (
    <ThemeContext.Consumer>
      { theme => <button {...props} theme={theme}>{theme}</button> }
    </ThemeContext.Consumer>
  )
}
复制代码

能够看到ThemeButton中直接获取到了theme属性,并无经过Toolbar来传递props,这正是Context作的事。ide

上面的例子中出现新的API:React.createContext、Provider、Consumer。接下来一一介绍下。函数

React.createContext

const {Provider, Consumer} = React.createContext(defaultValue);
复制代码

React.createContext 建立了 { Provider, Consumer } 这一对对象。当你使用Consumer来获取数据时,它会匹配在最近的一个对应的Provider。学习

defaultValue仅用在Consumer没有匹配到Provider时,Consumer就会使用defaultValue。ui

Provider

<Provider value={/* some value */}>
复制代码

Provider是一个容许Consumer订阅context变化的组件。this

Provider接受一个value属性,当它变化时,它后代Consumer组件也会相应的接受到变化的值。

Consumer

<Consumer>
  {value => /* render something based on the context value */}
</Consumer>
复制代码

Consumer是一个订阅context变化的组件。它须要在节点中传入一个函数,函数接受一个当前的context值,返回一个React节点。

一旦父节点中的Provider改变context值,Consumer会从新渲染。这边要注意的是及时Consumer父组件中shouldComponentUpdate返回false,Consumer包含的组件仍是会进行更新。

API至关简洁明了,最后看个例子巩固下。

import React, {Component} from 'react'
const ThemeContext = React.createContext({
  theme: 'light',
  changeTheme: () => {}
})

class App extends Component {
  constructor () {
    super()
    this.changeTheme = this.changeTheme.bind(this)
    this.state = {
      theme: 'light',
      changeTheme: this.changeTheme
    }
  }
  changeTheme () {
      this.setState({
          theme: 'dark'
      })
  }
  render () {
    return (
      <ThemeContext.Provider value={this.state}>
        <Toolbar />
      </ThemeContext.Provider>
    )
  }
}

function Toolbar () {
  return (
    <div>
      <ThemeButton />
    </div>
  )
}

function ThemeButton (props) {
  return (
    <ThemeContext.Consumer>
      { ({theme, changeTheme}) => <button {...props} onClick={changeTheme}>{theme}</button> }
    </ThemeContext.Consumer>
  )
}
复制代码

Portals

Portals 提供一种把组件子元素渲染到其余Dom节点下的方法

ReactDOM.createPortal(child, container)
复制代码

第一个参数就是可渲染的react元素, 第二个参数就是要渲染在其下的节点。

import React, {Component} from 'react'
import ReactDOM from 'react-dom';

function App () {
    return (
        <div> <Modal> {[1, 2, 3, 4]} </Modal> </div>
    )
}

class Modal extends Component {
  constructor(props) {
    super(props);
    this.el = document.createElement('div');
  }
  componentDidMount() {
    document.body.appendChild(this.el)
  }
  componentWillUnmount() {
    document.body.removeChild(this.el)
  }
  render() {
    return ReactDOM.createPortal(
      this.props.children,
      this.el,
    )
  }
}
复制代码

上面的例子中数组内容会被渲染到document.body下的一个div中,而不是渲染到App渲染的div中。这就实现了把元素渲染到其余DOM节点下的功能。

这边要注意的是Modal的事件冒泡仍是会通过App中元素,而不是直接到body去。

总结

本文介绍了两个新API:Context和Portals。这两个使用起来仍是比较简单的,可是仍是要项目应用才能找到最佳实践。

相关文章
相关标签/搜索