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後又多了一個異常,我們一一看下。
[更新圖片,此處特別感謝 @wuzhengyan2015 指正]掛載
生命週期的執行順序
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
前執行;更新元件時,該靜態方法會在shouldComponentUpdate
前執行。
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
【未完待續】