基礎(chǔ)
React.Component
1.state更新或者父組件state更新都會觸發(fā)
React.PureComponent
1.state更新或者父組件傳遞過來的props有更新
React.PureComponent with Context
1.如果不注冊contextType亿卤,則與React.PureComponent一致涵但。
2.如果注冊了contextType,則與React.Component一致
如果在視圖中使用context value昭抒,當(dāng)context value有改變時栋豫,視圖會更新挤安,但不會觸發(fā)componentWillUpdate/componentDidUpdate
不注冊contextType
parent component
import React from 'react'
const MyContext = React.createContext({});
class Parent extends React.Component {
state={
num:1
};
add=()=>{
let num = this.state.num;
num+=1;
this.setState({num:num})
};
render(){
return (
<MyContext.Provider value={this.state}>
<div>
<div>{this.state.num}</div>
<button onClick={this.add}>+1s</button>
<Child />
</div>
</MyContext.Provider>
)
}
}
export default Parent
child component
class Child extends React.PureComponent {
componentWillUpdate(nextProps, nextState, nextContext) {
console.log('componentWillUpdate',nextProps, nextState, nextContext)
}
componentDidUpdate(prevProps, prevState, snapshot) {
console.log('componentDidUpdate',prevProps, prevState, snapshot)
}
render(){
return (
<MyContext.Consumer>
{(state)=>
<div>
I'm child component,
<p>
this num from parent :{state.num}
</p>
</div>
}
</MyContext.Consumer>
)
}
}
//如何注冊contextType
//Child.contextType = MyContext;
進(jìn)階
SCU
shouldComponentUpdate
當(dāng)shouldComponentUpdate
返回true
時,React必須走到該節(jié)點并檢查它們丧鸯。
在diff
后發(fā)現(xiàn)元素不相等時蛤铜,React必須更新DOM,
當(dāng)diff
沒有變化時丛肢,利用thunk
機(jī)制可以使它不必更新DOM围肥。
但是在PureComponent
中,shouldComponentUpdate
只是對state
或props
的值進(jìn)行淺對比蜂怎,比如下方代碼:
class ListOfWords extends React.PureComponent {
render() {
return <div>{this.props.words.join(',')}</div>;
}
}
export default class WordAdder extends React.Component {
constructor(props) {
super(props);
this.state = {
words: ['marklar'],
};
this.handleClick = this.handleClick.bind(this);
}
handleClick() {
// This section is bad style and causes a bug
const words = this.state.words;
words.push('marklar');
this.setState({words: words});
}
render() {
return (
<div>
<button onClick={this.handleClick} />
<ListOfWords words={this.state.words} />
</div>
);
}
}
點擊button之后穆刻,ListOfWords
組件并不會更新。
因此避免此問題的最簡單方法是避免你正在使用的state
或props
值發(fā)生突變(mutating)杠步,而是比如這樣返回一個新值/新址:
- words.push('marklar');
- this.setState({words: words});
+ this.setState({words: words.concat('marklar')});
// 或者 ES6寫法
+ this.setState({words: [...words,'marklar']})
PS:這也是為什么React新版文檔內(nèi)變量全部換成
const
聲明了
優(yōu)化
手動對比shouldComponentUpdate
1.有時組件需要選擇性更新氢伟,而不能完全依賴PureComponent榜轿,經(jīng)常會這么寫:
shouldComponentUpdate(nextPrpops) {
return JSON.stringify(nextPrpops.data) !== JSON.stringify(this.props.data)
}
只有在this.props.data
數(shù)據(jù)被改變時才更新,這樣寫在小數(shù)據(jù)場景下本身是沒有問題的朵锣,
但是如果在大數(shù)據(jù)的場景下可能會有問題谬盐,使用JSON.stringify
暴力轉(zhuǎn)譯會非常耗時。
2.如果第一條的id
不一樣就表示數(shù)據(jù)變化了行不行诚些,顯然在某種情況下是存在的飞傀,但不夠嚴(yán)謹(jǐn)。
shouldComponentUpdate(nextPrpops) {
return nextPrpops.data[0].id !== this.props.data[0].id
}
3.將data
的比對轉(zhuǎn)換成current
的比對
shouldComponentUpdate(nextPrpops) {
return nextPrpops.current !== this.props.current
}
4.給一個requestId
跟蹤data
诬烹,后面就只比對requestId
砸烦。
this.setState({
data,
requestId: guid()
})
shouldComponentUpdate(nextPrpops) {
return nextPrpops.requestId !== this.props.requestId
}
上面的寫法可能都有問題,但主要想說的是寫代碼的時候可以想想是不是可以“將復(fù)雜的比對绞吁,變成簡單的比對”
自定義shallowEqual
函數(shù)
function shallowEqual(objA: mixed, objB: mixed): boolean {
// 第一關(guān):基礎(chǔ)數(shù)據(jù)類型直接比較出結(jié)果
if(is(objA, objB)) {
return true
}
// 第二關(guān):只要有一個不是對象數(shù)據(jù)類型就返回false
if(
typeof objA !== 'object' ||
objA === null ||
typeof objB !== 'object' ||
objB === null ||
){
return false
}
// 第三關(guān):如果兩個都是對象類型幢痘,比較兩者的屬性數(shù)量
const keysA = Object.keys(objA);
const keysB = Object.keys(objB);
if (keysA.length !== keysB.length) {
return false;
}
// 第四關(guān):比較兩者的屬性是否相等,值是否相等
for (let i = 0; i < keysA.length; i++) {
if (
!hasOwnProperty.call(objB, keysA[i]) ||
!is(objA[keysA[i]], objB[keysA[i]])
) {
return false;
}
+ }else {
+ if(!deepEqual(objA[keyA[i]], objB[keysA[i]])){
+ return false
+ }
}
// 默認(rèn)返回true
return true
}
shallowEqual函數(shù)源代碼:react/package/shared/shallowEqual.js