好比我们人除了短暂的生与死那一瞬之外,生命中剩下的时间都用在了每天活着的状态,对于React中的组件来讲,占其总生命周期最久的就是不断更新的状态了。然而更新是要不断消耗资源的,我们当然希望它能不更新就不更新同时能够保持正常的运作。但是现在的情况是组件发生re-render是件很轻易的事,稍微改点什么东西就会导致各种程度的re-render,即便有些组件根本没有re-render的必要,显然这样的情况是需要以某种方式去改善的。
所以,react提供了shouldComponentUpdate(nextProps, nextState)
这个函数,此函数没有被重写的话默认返回true
(这也就是为什么组件一言不和就re-render,因为在可能需要re-render的时候,不管最终需要不需要re-render,组件永远re-render肯定不会出错),但是我们可以自行重写这个函数,让它在某些情况下返回false
即在这些情况下组件不需要re-render,只要我们有足够把握。那我们怎样才能变得有把握呢?在这之前我们需要知道组件何时会走生命周期中更新的那一部分流程。
组件更新的流程
componentWillReceiveProps(nextProps)
shouldComponentUpdate(nextProps, nextState)
componentWillUpdate()
render()
componentDidUpdate()
触发组件更新的条件
- 外部条件:父组件的re-render也会导致其子组件走更新流程(不管此时父组件传给子组件的
props
有没有改变),这种方式则是要走componentWillReceiveProps(nextProps)
这步的,这里我们留意一下这个方法的参数nextProps
。 - 内部条件:
this.setState
,这个自不消多说,这是组件主动更新的唯一(应该是吧?)方式,只要调用这个方法组件就会走更新的流程,但是通过这种方式触发更新流程是不会走componentWillReceiveProps(nextProps)
,这一部分的,至于为什么,我也没弄清楚,但待会儿还是会说下自己的猜想。 - 其他条件:抱歉,刚接触不久,暂时只想到上面两个。
用一张图来总结下上面的内容
刚才我有说到当父组件的re-render时会导致其子组件走更新流程不管此时父组件传给子组件的props
有没有改变,现在我们通过一个简单的例子来证明:
//子组件
class SubComp extends React.Component {
constructor(props) {
super(props)
this.state = {
isUpdate: false
}
}
componentDidMount() {
console.log(this.props.order + ' component Mounted')
}
componentWillReceiveProps(nextProps) {
console.log(this.props.order + ' component will RecieveProps')
}
componentWillUpdate() {
console.log(this.props.order + ' component will update' )
}
componentDidUpdate() {
console.log(this.props.order + ' component did update' )
}
render() {
console.log(this.props.order + ' component render start')
/*接受一个从父元素传来的props.order*/
return(
<button onClick={() => {this.setState({isUpdate:true})}}>
{this.props.order+ ' button updated? : ' + this.state.isUpdate}
<br />
</button>
)
}
}
//父组件
class SupComp extends React.Component {
constructor(props) {
super(props)
}
componentDidMount() {
console.log('SupComponent Mounted')
}
componentWillReceiveProps(nextProps) {
console.log('SupComponent will RecieveProp' )
}
componentWillUpdate() {
console.log('SupComponent will update' )
}
componentDidUpdate() {
console.log('SupComponent did update' )
}
render() {
//渲染三个子组建,并提供一个按钮,当按钮被点击时强行update
console.log('SupComponent render start')
return (
<div>
<SubComp order="1st" />
<br />
<SubComp order="2nd" />
<br />
<SubComp order="3rd" />
<br />
<button onClick={() => {this.forceUpdate()}}>force update</button>
</div>
)
}
}
ReactDOM.render(
<SupComp />,
document.getElementById('example')
)
我们先来看下外部条件触发的情况,点击按钮让父元素强行update的re-render过程中,其子元素也会走update流程
再来看下内部通过
this.setState()
触发的情况,点击子组件本身改变this.state
的里的一个属性值好了,要证明的东西通过实例证明了,下面说两点关于“所以然”的理解或者说猜想
componentWillReceiveProps(nextProps)
与shouldComponentUpdate(nextProps, nextState)
两个函数的参数的关系
显然,在同一个更新周期内两个nextProps
是同一个东西,内容完全一样,都是当次更新周期内父组件传过来的props
;那么,shouldComponentUpdate(nextProps, nextState)
比componentWillReceiveProps(nextProps)
多出来的nextState
哪里来的呢?或者说,为什么componentWillReceiveProps(nextProps)
没有nextState
呢?先说前者,刚才我们讲了两种进入shouldComponentUpdate(nextProps, nextState)
的方式,nextProps
由父组件的re-render负责传入,那nextState
自然是由另外一种方式this.setState
传入,那有没有可能nextState
也由父组件的re-render传入呢?我猜应该是没有可能的,就我目前对react的学习来看,没见到有人能做在父组件里面调用子组件的setState()
这种操作,要如我所猜的话,就刚好解答了那上面“或者说”后面的那个问题。
好了,继续下一个问题
子组件的this.setState()
导致的update为甚么不走 componentWillReceiveProps(nextProps)
的流程
我们看下程墨的《深入浅出React和Redux》(感觉很不错的,值得入门推荐)P30对这个问题的解释
通过
this.setState
方法触发的更新过程不会调用这个函数,这是这个函数适合根据新的props
值(也就是参数nextProps
)来计算出是不是要更新内部状态state
。更新组件内部状态的方法是this.setState
,如果this.setState
的调用导致componentWillReceiveProps(nextProps)
的再一次调用,那就是一个死循环了。
上面的解释我不大理解,我不理解里面所说的内部状态state
是不是就是子组件本身的this.state
还是说是什么除了这个this.state
之外组件还有什么内部对使用者透明的state
。如果是前者,根据nextProps
计算是不是要更新内部状态state
,怎么计算?那意味着nextProps
和state
要发生关系,那就要证明存在这样的父子组件模型,满足父组件的re-render传递nextProps
的同时会改变子组件的state
,这个问题等价于前面“那有没有可能nextState
也由父组件的re-render传入?”这个问题;或者起码要证明子组件的this.setState
能够影响父组件传来的props
的,如果证明不了就说明不是this.setState
不会调用componentWillReceiveProps(nextProps)
这个函数,而是this.setState
操作中根本无法调用这个函数或者说调用了也没有任何意义(父元素传过来的props
没有任何改变,走那一步有什么用)。如果是除了这个this.state
之外组件还有什么内部对使用者透明的state
这种情况,那还是理解不了,哎,好乱啊。
总结吧
上面一系列的问题,应该都可以归结为一个问题:子组件update时shouldComponentUpdate(nextProps, nextState)
里的两个参数是不是分别独立来自父组件的re-render和自身this.state
?或者说每次调用shouldComponentUpdate(nextProps, nextState)
时nextProps===this.props
和nextState===this.state
两个等式是不是必定最少有一个成立。
最后说一下开头抛出的问题:“那我们怎样才能变得有把握呢?”,就是,比如说父组件X传给子组件的props
组成的集合为A,子组件自身this.state
为集合B,当前子组件render的内容依赖A里的一些元素也可能同时依赖B里的一些元素,前者组成的集合为a,后者为b,显然a是A的子集,b是B子集,对于A-a和B-b那部分我们就不用管啦,因为我们不依赖,只要我们自身依赖的那些元素的值与上一次相较没有发生变化,就可以放心大胆地(应该是这样吧?)返回 false
啦。
也不知道上面扯的错多少,继续学习,继续实践,真的是,学了不实践几乎等于白学,实践才能深刻。当然,上面扯的肯定远算不上深刻。
9.30更新
其实之前就一直有在想,我们能在componentWillReceiveProps(nextProps)
这个函数里做什么呢?今天突然想到一个,可以在里面执行this.setState()
,这样的话我们就可以把UI上依赖于this.props
的内容全都剥离出来统一放到this.state
里,那么对于每次由于父组件re-render引起的update,我们都在componentWillReceiveProps(nextProps)
将我们需要的部分this.setState()
,然后shouldComponentUpdate(nextProps, nextState)
里只用关注nextState
的部分了,只是这样一来this.state
里的东西就多了,而且由于原来父组件传过来的props
变成自身的this.state
了,而this.state
可以随意更改的,这样是不是存在误操作的风险。所以,我也不知道上面的做法有无意义,哈哈哈哈