componentWillUpdate/componentDid
基础
React.Component
1.state更新或者父组件state更新都会触发
React.PureComponent
1.state更新或者父组件传递过来的props有更新
React.PureComponent with Context
1.如果不注册contextType,则与React.PureComponent一致。
2.如果注册了contextType,则与React.Component一致
如果在视图中使用context value,当context value有改变时,视图会更新,但不会触发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;
进阶
SCU
shouldComponentUpdate
当shouldComponentUpdate
返回true
时,React必须走到该节点并检查它们。
在diff
后发现元素不相等时,React必须更新DOM,
当diff
没有变化时,利用thunk
机制可以使它不必更新DOM。
但是在PureComponent
中,shouldComponentUpdate
只是对state
或props
的值进行浅对比,比如下方代码:
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
值发生突变(mutating),而是比如这样返回一个新值/新址:
- words.push('marklar');
- this.setState({words: words});
+ this.setState({words: words.concat('marklar')});
// 或者 ES6写法
+ this.setState({words: [...words,'marklar']})
PS:这也是为什么React新版文档内变量全部换成
const
声明了
优化
手动对比shouldComponentUpdate
1.有时组件需要选择性更新,而不能完全依赖PureComponent,经常会这么写:
shouldComponentUpdate(nextPrpops) {
return JSON.stringify(nextPrpops.data) !== JSON.stringify(this.props.data)
}
只有在this.props.data
数据被改变时才更新,这样写在小数据场景下本身是没有问题的,
但是如果在大数据的场景下可能会有问题,使用JSON.stringify
暴力转译会非常耗时。
2.如果第一条的id
不一样就表示数据变化了行不行,显然在某种情况下是存在的,但不够严谨。
shouldComponentUpdate(nextPrpops) {
return nextPrpops.data[0].id !== this.props.data[0].id
}
3.将data
的比对转换成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
}
上面的写法可能都有问题,但主要想说的是写代码的时候可以想想是不是可以“将复杂的比对,变成简单的比对”
自定义shallowEqual
函数
function shallowEqual(objA: mixed, objB: mixed): boolean {
// 第一关:基础数据类型直接比较出结果
if(is(objA, objB)) {
return true
}
// 第二关:只要有一个不是对象数据类型就返回false
if(
typeof objA !== 'object' ||
objA === null ||
typeof objB !== 'object' ||
objB === null ||
){
return false
}
// 第三关:如果两个都是对象类型,比较两者的属性数量
const keysA = Object.keys(objA);
const keysB = Object.keys(objB);
if (keysA.length !== keysB.length) {
return false;
}
// 第四关:比较两者的属性是否相等,值是否相等
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
+ }
}
// 默认返回true
return true
}
shallowEqual函数源代码:react/package/shared/shallowEqual.js