React生命周期中的componentWillReceiveProps和shouldComponentUpdate

好比我们人除了短暂的生与死那一瞬之外,生命中剩下的时间都用在了每天活着的状态,对于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),这一部分的,至于为什么,我也没弄清楚,但待会儿还是会说下自己的猜想。
  • 其他条件:抱歉,刚接触不久,暂时只想到上面两个。
    用一张图来总结下上面的内容
    image.png

    刚才我有说到当父组件的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流程

image.png

image.png

再来看下内部通过this.setState()触发的情况,点击子组件本身改变this.state的里的一个属性值
image.png

好了,要证明的东西通过实例证明了,下面说两点关于“所以然”的理解或者说猜想

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,怎么计算?那意味着nextPropsstate要发生关系,那就要证明存在这样的父子组件模型,满足父组件的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.propsnextState===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可以随意更改的,这样是不是存在误操作的风险。所以,我也不知道上面的做法有无意义,哈哈哈哈

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,271评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,275评论 2 380
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,151评论 0 336
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,550评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,553评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,559评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,924评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,580评论 0 257
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,826评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,578评论 2 320
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,661评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,363评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,940评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,926评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,156评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,872评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,391评论 2 342

推荐阅读更多精彩内容