React16.3更新了很多新的内容:生命周期、Context、React.createRef()、Portals等等。对于更新飞快的前端来说,我们应该已经习惯了要不断学习╮(╯▽╰)╭。本文将介绍官方文档两个结合新内容Context和Portals。
Context
Context 提供了一种不用手动一层层传递props就能在组件树中传递数据的方式。
在传统的React应用中,数据通常从父组件通过props一层层传递给子组件,但是这种方式在属性需要被很多组件用到的时候会显得很麻烦。
所以Context就被设计来在组件树中共享一些“全局”数据。
直接看例子吧
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做的事。
上面的例子中出现新的API:React.createContext、Provider、Consumer。接下来一一介绍下。
React.createContext
const {Provider, Consumer} = React.createContext(defaultValue);
React.createContext 创建了 { Provider, Consumer } 这一对对象。当你使用Consumer来获取数据时,它会匹配在最近的一个对应的Provider。
defaultValue仅用在Consumer没有匹配到Provider时,Consumer就会使用defaultValue。
Provider
<Provider value={/* some value */}>
Provider是一个允许Consumer订阅context变化的组件。
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。这两个使用起来还是比较简单的,但是还是要项目应用才能找到最佳实践。