React16.3更新了不少新的內容:生命週期、Context、React.createRef()、Portals等等。對於更新飛快的前端來講,咱們應該已經習慣了要不斷學習╮(╯▽╰)╭。本文將介紹官方文檔兩個結合新內容Context和Portals。前端
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。接下來一一介紹下。函數
const {Provider, Consumer} = React.createContext(defaultValue);
複製代碼
React.createContext 建立了 { Provider, Consumer } 這一對對象。當你使用Consumer來獲取數據時,它會匹配在最近的一個對應的Provider。學習
defaultValue僅用在Consumer沒有匹配到Provider時,Consumer就會使用defaultValue。ui
<Provider value={/* some value */}>
複製代碼
Provider是一個容許Consumer訂閱context變化的組件。this
Provider接受一個value屬性,當它變化時,它後代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 提供一種把組件子元素渲染到其餘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。這兩個使用起來仍是比較簡單的,可是仍是要項目應用才能找到最佳實踐。