React16 後的各功能點是多個版本陸陸續續迭代增加的,本篇文章的講解是建立在16.6.0
版本上
本篇文章主旨在介紹 React16 之後版本中新增或修改的地方,所以對於 React16 之前版本的功能,本篇文章當作您已充分了解了,不再贅述
更新概覽
從 React v16.0 ~ React v16.6 的更新概覽(只涉及部分常用api):
- React v16.0
- render 支持返回數組和字符串
- 支持自定義 DOM 屬性
- 減少文件體積
- React v16.3
- createContext
- createRef、
- 生命週期函數的更新
- React v16.4
更新 getDerivedStateFromProps
- React v16.6
- memo
- lazy
- Suspense
- static contextType
- static getDerivedStateFromError()
- React v16.7(~Q1 2019)
Hooks
接下來將針對影響較大,使用頻率較高的更新點逐一講解。
純函數的PureComponent
我們知道,對 React 組件的性能優化,shouldComponentUpdate
函數是很重要的一啪,所以 React 纔會在 React.Component
的基礎上增加了React.PureComponent
,但是對於非class類的純函數寫法,卻沒法增加這樣的便捷處理。
對於這個問題,React16.6 增加了React.memo
這個高階組件
一般使用方式:
const C = React.memo(props => {
// xxx
})
React.memo
的實現類似React.PureComponent
,所以它內部是對對象進行淺比較。React.memo
允許你自定義比較方法,如下:
// 函數的返回值爲 true 時則更新組件,反之則不更新
const equalMethod = (prevProps, nextProps): boolean => {
// 定義你的比較邏輯
}
const C = React.memo(props => {
// xxx
}, equalMethod)
新的生命週期函數是怎樣的
React生命週期分爲三個階段:掛載、更新、卸載,React16後又多了一個異常,我們一一看下。
掛載
生命週期的執行順序
constructor
static getDerivedStateFromProps
render
componentDidMount
render
和componentDidMount
較 React16 之前無變化。對於掛載過程,我們着重看下constructor
、componentWillMount
和static getDerivedStateFromProps
。
constructor
- 初始化 state
注意:應避免使用props給state賦值,這樣的話, state 的初始化可以提到constructor外面處理
constructor(props) {
super(props);
this.state = {
x: 1,
// y: props.y, // 避免這樣做,後面我們會講應該怎樣處理
}
}
- 給方法綁定this
constructor(props) {
super(props);
this.handleClick = this.handleClick.bind(this);
}
但是,以上兩件事放到constructor
外面處理會更簡單些,如下:
class C extends React.Component {
state = {
x: 1
}
handleClick = (e) => {
// xxx
}
}
所以,React16 以後用到constructor
的場景會變少。
componentWillMount
可以看到,componentWillMount
在 react16 中被“刪掉”了(這樣說其實是有問題的,因爲 react 並未真正刪除該生命週期函數,只是告誡開發者,該函數在未來版本中會被廢棄掉),那麼問題就出現了,原先在這個生命週期中的做的事情,現在該放到哪裏去做呢?
首先問自己一個問題,原先的時候都在這個生命週期裏做什麼?答案是大部分時候會在這裏做AJAX請求,然後執行 setState 重新渲染。
然而在componentWillMount
裏做AJAX請求實在不是一個明智之舉,因爲對於同構項目中,componentWillMount
是會被調用的。
還有人會在這裏面初始化 state ,關於 state 的初始化,請參看樓上小節。
綜上所述,componentWillMount
其實本來沒有什麼主要作用,如果你的代碼規範,去掉的話,不會對現在的項目產生什麼影響。
static getDerivedStateFromProps
上面我們講到,應避免使用props給state賦值,但是在 React16 前我們都是這麼做的,現在如果不讓這麼操作了,那該在哪裏處理這塊邏輯呢? React16 給出的答案就是 static getDerivedStateFromProps
,掛載組件時,該靜態方法會在render
前執行。
class C extends React.Component {
state = {
y: 0
}
static getDerivedStateFromProps(props, state): State {
if(props.y !== state.y) {
return {
y: props.y
};
}
}
}
getDerivedStateFromProps
的返回值將作爲setState的參數,如果返回null,則不更新state,不能返回object 或 null 以外的值,否則會警告。
getDerivedStateFromProps
是一個靜態方法,是拿不到實例this
的,所以開發者應該將該函數設計成純函數。
這樣,有沒有發現componentWillReceiveProps
也就沒有用武之地了?是的,React16 把它也“刪掉”了(這樣說其實是有問題的,因爲 react 並未真正刪除該生命週期函數,只是告誡開發者,該函數在未來版本中會被廢棄掉,建議使用更好的getSnapshotBeforeUpdate
或 getDerivedStateFromProps
)
更新
生命週期函數的執行順序
static getDerivedStateFromProps
shouldComponentUpdate
render
getSnapshotBeforeUpdate
componentDidUpdate
static getDerivedStateFromProps
前面已經介紹過了,而其他的幾個生命週期函數與 React16 之前基本無異,所以這裏主要介紹下getSnapshotBeforeUpdate
。
getSnapshotBeforeUpdate
在 react 更新 dom 之前調用,此時 state 已更新;
返回值作爲 componentDidUpdate 的第3個參數;
一般用於獲取 render 之前的 dom 數據
- 語法
class C extends React.Component {
getSnapshotBeforeUpdate (prevProps, prevState): Snapshot {
}
componentDidUpdate(prevProps, prevState, snapshot) {
// snapshot 是從 getSnapshotBeforeUpdate 的返回值,默認是 null
}
}
getSnapshotBeforeUpdate
的使用場景一般是獲取組建更新之前的滾動條位置。
卸載
componentWillUnmount
較之前無變化
異常
componentDidCatch
這個函數是 React16 新增的,用於捕獲組件樹的異常,如果render()
函數拋出錯誤,則會觸發該函數。可以按照 try catch
來理解和使用,在可能出現錯誤的地方,使用封裝好的包含 componentDidCatch
生命週期的組建包裹可能出錯的組件。
class PotentialError extends React.Component {
state = {
error: false,
}
componentDidCatch(error, info) {
console.error(info);
this.setState({
error
});
}
render() {
if (this.state.error) {
return <h1>出錯了,請打卡控制檯查看詳細錯誤!</h1>;
}
return this.props.children;
}
}
如:
const Demo = () => (
<PotentialError>
<div>{{a: 1}}</div>
</PotentialError>
)
這樣,Demo
組件即使直接使用對象作爲子組件也不會報錯了,因爲被 PotentialError
接收了。
新生命週期的完整demo
看看穿上新生命週期這身新衣服後的樣子吧
import React from 'react'
export default class MyComponent extends React.Component {
constructor(props) {
super(props);
// 初始化state方式(1)
this.state = {
}
}
static defaultProps = {
}
// 初始化state方式(2)
state = {
}
static getDerivedStateFromProps(props, state) {
return state
}
componentDidCatch(error, info) {
}
render() {
}
componentDidMount() {
}
shouldComponentUpdate(nextProps, nextState) {
}
getSnapshotBeforeUpdate(prevProps, prevState) {
}
componentDidUpdate(prevProps, prevState, snapshot) {
}
componentWillUnmount() {
}
}
Suspense
Hooks
time slicing
【未完待續】