上节课我们展开讨论了一下forceUpdate
,并且对比了forceUpdate
和setState
的区别,那么这节课我们看说最后一个入口父组件render
这个流程。
回顾
在开始新课程之前,我们先来对上节课的几个重要知识点来做一个简单的回顾:
forceUpdate
比setState
少一个shouldComponentUpdate
环节forceUpdate
与setState
的本质区别就是forceUpdate
不对state
做任何更改forceUpdate
不受shouldComponentUpdate
方法的限制
以上便是上节课的主要知识点,接下来我们来开始进行这节课的内容。
父组件
看到这个词我们有没有觉得仿佛并不是那么陌生?既然我们用了父组件这个词,那么是不是说明组件之间是可以形成父子关系的?就好像类一样,有父类和子类。
好我们先回到我们的案例当中,在前几节课中我们在Count
组件中写了大量的代码,几乎所有生命周期的钩子都已经写进去了。现在我们还在这个组件中来继续测试的话代码量太大,我们就不太方便了。那么我们怎么做?我们重新创建一个组件,比如叫CountSub
。
class Count extends React.Component {...} class CountSub extends React.Component { render() { return ( <div> </div> ); } } ReactDOM.render(<CountSub />, document.getElementById("test"));
大家看,我这边定义了一个新的组件CountSub
,这样的话我是不是好像根本不会挂载Count
组件啊?因为饿根本就没有在ReactDOM.render
中做跟Count
组件有关的事情。也就是说我们定义了一个Count
组件但是压根没有用这个组件。
先别着急,我们再定义一个组件叫Test
组件,我们让Test
组件和CountSub
组件形成父子关系,怎么形成父子关系呢?我们来看代码:
class CountSub extends React.Component { render() { return ( <div> <div>CountSub</div> <Test /> </div> ); } } class Test extends React.Component { render() { return ( <div>Test</div> ); } } ReactDOM.render(<CountSub />, document.getElementById("test"));
大家看一下上面这段代码,我是不是在CountSub
组件render
方法中把Test
组件的组件标签写进去了?这是个什么意思呢?这就是在CountSub
组件中来使用了Test
组件。
组件挂载是个什么流程?
react
解析组件标签react
发现组件是个类组件react
实例化这个组件类react
通过组件实例对象调用render
方法render
方法返回jsx
代码执行结果。
好,就先回忆到这。那么大家想一下,在CountSbu
组件的render
方法中jsx
代码执行的过程中遇到了一个组件标签会怎么办?报错?忽略掉?还是说会进一步去解析组件标签呢?
我们来回想一下jsx
的语法规则中的其中一块:
jsx
中的标签在被渲染时会检查标签首字母- 如果
jsx
中标签首字母小写,那么则将该标签渲染为HTML
中同名标签,若无同名标签则会报错。 - 如果
jsx
中标签首字母大写,那么react
就去渲染对应的组件,若该组件没有定义,则报错。
那么现在我们是不是就知道jsx
遇到组件标签会怎么做了吧?那就会直接进一步去渲染这个组件标签所对应的组件。那么我们来看一下上面这段代码是什么效果:
这一下我们同时展示出了CountSub
组件和Test
组件,同过开发工具中可以看出Test
组件的层级是包含在CountSub
组件内层的所以说CountSub
组件是Test
组件的父组件。这样这两个组件的父子关系就形成了。
流程图
那么我们既然已经知道如何来将两个组件形成父子关系了,那么就好办了。来顺着这个流程图继续来理我们的流程。
父子组件的更新案例
class CountSub extends React.Component { state = {count: 10}; render() { return ( <div> <div>CountSub组件</div> <button onClick={this.clear}>清空</button> <Test count={this.state.count}/> </div> ); } clear = () => {this.setState({count: 0});}; } class Test extends React.Component { render() { return ( <div>Test 将数量变更为了{this.props.count}</div> ); } }
我们来看这段代码,我们在CountSub
组件中初始化了state
,让state
的count
属性为 10 ,但是我们看一下,我们是不是根本没有在CountSub
组件中来展示我们state
中的值啊,不过呢我们在CountSub
组件中挂载了Test
组件,那么我想要让Test
组件来展示CountSub
组件的state
中的值,我们应该怎么做?是不是通过props
?所以当我在CountSub
组件中挂载Test
组件的时候把state
的值通过标签属性传入了Test
组件,然后在Test
组件中通过props
来拿到CountSub
组件的state
中的值并展示出来。
那么好,现在我想要更新了。我给CountSub
组件添加一个按钮,并且绑定了onClick
回调,只要我一点击按钮就会修改state
然后驱动页面更新,页面靠什么更新?是不是重新调用render
方法?render
方法是不是要返回jsx
代码执行结果?jsx
代码一执行是不是检查到了有Test
组件标签,而且还把state
中的值通过标签属性传给了Test
组件?那么是不是就要解析Test
组件标签?然后就使得整个页面实现了一次更新。我们就是这个流程对不对?好我们来看一下效果:
第一次挂载,因为有初始state
,所以传入Test
组件的值也就是 10 ,那么我们来点击按钮:
正如我们所预期的那样,页面完成了一次更新。但是我们的目的是看这个玩意儿吗?我们不是要探讨生命周期吗?那么好,来看流程图:
组件更新流程
这个父组件render
的入口应该怎么触发呢?和另外两个入口没有什么区别,就是一旦父组件调用了render
方法,就会触发这个流程。当触发了这个流程之后就会直接调用componentWillReceiveProps
方法。这个方法是什么意思?分开来看,字面上意思就是:组件、即将、收到、属性。
那么我们来问一个问题,这里componentWillReceiveProps
方法是那个组件的?是不是Tets
组件的?因为我们的案例中是Test
组件接收到了props
啊,也就是说这个componentWillReceiveProps
方法是子组件调用的。
那么我再问大家componentWillReceiveProps
方法是在什么时候调用的?是不是在子组件接收到props
之前调用的?你看这不是说了嘛,即将收到属性,所以说这个方法是在子组件接收到props
之前就会被调用。
那么如果我们在子组件中要是写了componentWillReceiveProps
方法的话,会不会被执行呢?我们测试一下:
class CountSub extends React.Component {...} class Test extends React.Component { componentWillReceiveProps(){ console.log("Test-componentWillReceiveProps"); } render() {...} }
我们在之前的效果中也看出来了,我们的Test
组件已经接收到了props
,而componentWillReceiveProps
方法还是在接收到props
之前被调用,那么我们在Test
组件中写了componentWillReceiveProps
方法,那是不是一定会被调用?我们来看一下效果:
控制台里面空空如也,没调用啊,这是为什么呢?大概有两个原因:
componentWillReceiveProps
方法名称写错了- 真的就没有调用
我们检查了一下,我们的方法名确实没有写错,那么是不是就只剩一个可能性了,确实是没有调用。那么为什么?这里就是一个比较坑的点了,我们最一开始是不是就说了这边的三个入口都是生命周期中组件更新的流程?那么我们第一次打开页面的时候state
初始化,有更新吗?是不是没有更新啊?我第一次挂载了CountSub
组件,执行了CountSub
组件的render
方法,这个时候父组件虽然调用了render
,但是在render
返回结果之前,我们是不是第一次挂载了Test
组件?这一步是挂载流程并不是更新流程,所以说不会触发componentWillReceiveProps
方法的调用,那么我们这个时候点击一下按钮再看一下效果:
正如我们的预期一样,Test
组件中的componentWillReceiveProps
方法被调用了。或许大家对这一步会有些不理解,我更新了 state
,导致父组件重新调用render
方法,那么这一次render
执行到Test
组件标签的时候,难道不是挂载吗?那我们在Test
组件中加上componentDidMount
方法来测试一下。
class CountSub extends React.Component {...} class Test extends React.Component { componentWillReceiveProps(){ console.log("Test-componentWillReceiveProps"); } componentDidMount(){console.log("Test-componentDidMount");} render() {...} }
我来问大家,componentWillMount
是在什么时候调用?是不是在组件完成挂载之后立即调用?那么是不是就意味着我们组件每挂载一次,就会被调用一次?那么我们来看一下效果:
第一次挂载,控制台打印出我们的componentDidMount
被调用了,那么当我们点击按钮是不是改变了 state
?那么CountSub
是不是就要重新调用render
方法?那么render
方法中的Test
组件标签是不是就要被重新执行一次?这个时候如果Test
组件是被重新挂载了,那么是不是会再打印一次Test-componentDidMount
?那么我们看结果吧:
我点击了 12 次按钮,componentWillReceiveProps
方法被调用了 12 次,而componentDidMount
方法却仅仅只在第一次挂载的时候被调用了。所以说当我们点击按钮的时候是触发的更新流程,而且子组件并没有被重新挂载。
当然这个环节过去了,后面的环节就是我们前两节课说过的了,这里就不在赘述了,大家有遗忘的可以去翻一下前两节课的内容。
以上便是本节课的主要内容
总结
- 只有当父组件的
render
方法被重新调用才会触发更新流程 - 只有当父组件的
render
方法被重新调用才会触发componentWillReceiveProps
的调用 - 当父组件被重新调用的时候,子组件不会被重新挂载
componentWillReceiveProps
方法在子组件接收到props
之前调用
Copyright statement:The articles of this site are all original if there is no special explanation, indicate the source please when you reprint.
Link of this article:https://work.lynchow.com/article/lifecycle_prender_old/