這個 Lifecycle 其實非常直覺,看字面意思就是說「 決定 Lifecycle 是不是該 update ? 」
它在被呼叫的時間點為:改變 state 之後、執行 render()
之前。
所以如果沒有特別指定的話,預設會回傳 true
。
以下是一個按 click me
按鈕會增加 counter 的小程式:
const Counter = props => {
return <div>{props.counter}</div>;
}
class App extends Component {
constructor(props) {
super(props);
this.state = {
counter: 1
}
this.handleClick = this.handleClick.bind(this);
}
handleClick() {
this.setState({
counter: this.state.counter + 1
})
}
shouldComponentUpdate() {
console.log('shouldComponentUpdate is called'); // => 出現 1.
return true; // => 就是預設值,有寫沒寫都一樣
}
render() {
console.log('render'); // => 出現 2.
const {isShow} = this.state;
return (
<div>
<button onClick={this.handleClick}>Click me</button>
<Counter counter={this.state.counter}/>
</div>
)
}
}
render()
會發現每按一下按鈕,console 會依序出現 shouldComponentUpdate is called
跟 render
,代表先執行 shouldComponentUpdate
再執行 render
。
那如果我把 shouldComponentUpdate()
回傳值改為 false
呢?
會發現按鈕突然無法 render
了,沒錯!
shouldComponentUpdate()
的回傳值決定了 「 當 Component 的 state 改變時,要不要去 call render()
function 」
那既然 state 改變,不就要更新 UI 嗎? 還是說有什麼情況要把回傳值改成 false
呢?
有的,當遇到以下兩種情況,可以使用參數 nextProps
& nextState
來進行檢查:
state
or props
是否有更新,沒有更新就不必要浪費資源重新渲染nextProps
& nextState
nextState
改變後的 statenextState
就是改變後的 state,所以我們可以依據 state 的狀態,去決定要不要 call render()
,例如以下改成,如果 counter
超過 3
,畫面就不要再有變動:
shouldComponentUpdate(nextProps, nextState) {
console.log('nextState: ', nextState);
if (nextState.counter > 3) return false; // => 超過 3 就不要 call render()
return true;
}
看 console 可以知道,實際上 counter
已經增加到 6
了,但畫面上的 counter
數量還是停留在 3
nextProps
改變後的 propsnextProps
跟 nextState
很像,只是目標是 props,實際的用途比較常拿來做檢查用。
假設我們有個 <Title />
Component,外面還有包一個 Component。
<Title />
本身並不常更新,但如果 parent Component 要 call render()
,children Component 也會跟著 render()
。
所以當 <Title />
本身狀態並沒有改變的話,其實是沒必要的動作,此時就很適合用 nextProps
來進行檢查。
class Title extends Component {
shouldComponentUpdate(nextProps, nextState) {
// => 改變後的 props 跟原本一樣的話就不要 call render()
if (nextProps.title !== this.props.title) return true;
return false;
// [ 注意 ] 以下錯誤寫法: 不能只寫一個情況。
// if (nextProps.title === this.props.title) return false;
}
render() {
console.log('render title');
return <div>{this.props.title}</div>
}
}
PureComponent
以上可以知道 shouldComponentUpdate
能夠幫助我們做兩件事:
render()
props
or state
沒有變動時,不要 render()
就第 2 個條件來說,難道每次都要手動比對,沒有更方便的作法嗎?
有的!相較於以前介紹過的 React.Component
跟 functional Component
,還有一個叫做 「 PureComponent
」。
它的方便之處在於會幫你做 shallowly compares,意思是說會自動偵測 props
or state
的變動,沒有更新就不會 call render()
。
你可以把以下兩種寫法視為相同的:
// 1. 使用 PureComponent 會自動 shallowly compares
class Title extends PureComponent {
render() {
...
}
}
// 2. 自己寫 shouldComponentUpdate 判斷
class Title extends Component {
shouldComponentUpdate(nextProps, nextState){
return !shallowEqual(this.props, nextProps) || !shallowEqual(this.state, nextState);
}
render() {
...
}
}
這樣 PureComponent
聽起來不是超棒的嗎? 全部改成 PureComponent
就好了啊!
沒有這麼萬用喔,最好符合以下兩個條件,才使用 PureComponent
:
state
or props
很少變動的元件不然因為每次都進行 shallowEqual()
比對,如果元件一直頻繁更新,根本沒必要阻止 render()
,到頭來反而會拖慢效率。
state
or props
結構簡單的情況因為 PureComponent
只會幫你做 shallowEqual()
,如果是多層的陣列或物件並沒有用處
所以結構複雜的話可以考慮用 shouldComponentUpdate
搭配 library,例如 Immutable.js
參考資料:
( 以上內容大部分是 程式導師實驗計畫第三期 的學習筆記,如有錯誤歡迎糾正,非常感謝 🤓 )
Written on September 12th, 2019 by Yakim shu