关于react生命周期的一些我的理解(其中包括一些其余帖子的转载)

一、老版本的生命周期以及按顺序执行的流程
Initialization (初始化阶段)
这些方法会在组件初始化的时候被调用,只跟实例的建立有关。react

若是用 createReactClass 进行建立,则还有 getInitialState 这些生命周期函数,但不多使用,咱们这里不说起。算法

static defaultProps{} (getDefaultProps())
定义默认 props ,会和父组件传下来的 props 进行合并,且以父组件中的 props 优先级更高,至关于 {...defaultProps, props} 。redux

static propTypes{} (getInitialState())
定义 props 的数据类型,能够帮助咱们肯定其有效性,减小许多开发中没必要要的错误。浏览器

constructor()
在加载阶段前调用一次,进行 state 的初始化。性能优化

constructor(props){服务器

super(props)

}网络

super(props) 用来调用父类的构建方法。数据结构

Mounting(加载阶段)
componentWillMount()
新版中为 UNSAFE_componentWillMount() 。架构

只在组件加载时调用,整个生命周期只调用一次,之后组件更新也不会调用,此时能够修改 state。dom

render()
react 中最重要的生命周期函数,建立虚拟 dom,并进行 diff 算法,更新 dom 树也在此进行。因此不该该在此改变组件的状态或者与浏览器进行交互。

注意:这个函数不能缺乏,若是不建立虚拟 dom,能够 return null 。

componentDidMount()
组件加载完成后当即调用,整个生命周期只调用一次,能够获取到更新后的 DOM,在此处能够进行网络请求等。

Updating(更新阶段)
componentWillReceiveProps()
新版中为 UNSAFE_componentWillReceiveProps() 。

在组件加载后,若是有新的 props 传递过来,会先调用这个函数,能够在这里调用 setState() 修改 state 。

componentWillReceiveProps(nextProps)

shouldComponentUpdate()
react 中一个重要的性能优化点,组件接收到新的 props 或者 state ,返回 true 表示须要更新 dom,返回 false 阻止更新。

shouldComponentUpdate(nextProps, nextState)

componentWillUpdate()
新版中为 UNSAFE_componentWillUpdate() 。

组件加载时不调用,只有在组件须要更新(即 shouldComponentUpdate 返回 true )时调用。

componentWillUpdate(nextProps, nextState)

注意:不能在这个方法中调用 setState() 修改 state 。

不然会从新触发updating

render()
componentDidUpdate()
在组件更新完成后当即被调用,能够进行网络请求等。

componentDidUpdate(prevProps, prevState)

Unmouting(销毁阶段)
componentWillUnmount()
在组件被卸载和销毁以前调用,能够在这里处理任何须要的清理工做,好比解除定时器,取消已经发起的网络请求,清理在 componentDidMount 函数中建立的 DOM 元素。

componentWillUnmount()

Catching(捕获阶段)
componentDidCatch()
错误边界捕获,在v16.0刚推出的时候新增长的一个生命周期函数,用于捕获在 子组件树 中任意地方发生的JavaScript 错误,一个错误边界不能捕获它本身内部的错误。

componentDidCatch(error, info)

二、新版本中新增的生命周期以及对应的使用方法
首先,新版本中有三个生命周期被deprecate(去掉)了,他们分别是:

componentWillReceiveProps
componentWillMount
componentWillUpdate
getDerivedStateFromProps()

这个getDerivedStateFromProps是一个静态函数,因此函数体内不能访问this,简单说,就是应该一个纯函数,纯函数的话,输出彻底由输入决定。

static getDerivedStateFromProps(nextProps, prevState) {

      //根据nextProps和prevState计算出预期的状态改变,返回结果会被送给setState

}

每当父组件引起当前组件的渲染过程时,getDerivedStateFromProps会被调用,这样咱们有一个机会能够根据新的props和以前的state来调整新的state,若是放在三个被deprecate生命周期函数中实现比较纯,没有反作用的话,基本上搬到getDerivedStateFromProps里就好了;

注意:getDerivedStateFromProps不管是Mounting仍是Updating,也不管是由于什么引发的Updating,所有都会被调用。

getSnapshotBeforeUpdate()

这函数会在render以后执行,而执行之时DOM元素尚未被更新,给了一个机会去获取DOM信息,计算获得一个snapshot,这个snapshot会做为componentDidUpdate的第三个参数传入。

getSnapshotBeforeUpdate(prevProps, prevState) {

     console.log('#enter getSnapshotBeforeUpdate');

     return 'foo';

 } 

componentDidUpdate(prevProps, prevState, snapshot) {

    console.log('#enter componentDidUpdate snapshot = ', snapshot);

}

getSnapshotBeforeUpdate把snapshot返回,而后DOM改变,而后snapshot传递给componentDidUpdate。

总结: 用一个静态函数getDerivedStateFromProps来取代被清除的几个生命周期函数,就是强制开发者在render以前只作无反作用的操做,并且能作的操做局限在根据props和state决定新的state,而已。
这是进一步施加约束,防止开发者乱来

Q&A:
一、Willmount or didmount?

以前想过为何不在componentWillMount里写AJAX获取数据的功能,以前的观点是,componentWillMount在render以前执行,早一点执行早获得结果。

可是,要知道,在componentWillMount里发起AJAX,无论多快获得结果也赶不上首次render,

并且componentWillMount在服务器端渲染也会被调用到(固然,也许这是预期的结果),

这样的IO操做放在componentDidMount里更合适。在Fiber启用async render以后,更没有理由在componentWillMount里作AJAX,

由于componentWillMount可能会被调用屡次,谁也不会但愿无谓地屡次调用AJAX吧。

其实这个问题react的开发者早就有想过,因此才运营而生了后面的新的生命周期,既然不想放在这些很差的生命周期里面,那都不如直接让他们干脆没办法作。

二、何时用forceUpdate?

forceUpdate就是从新render。有些变量不在state上,可是你又想达到这个变量更新的时候,刷新render;

或者state里的某个变量层次太深,更新的时候没有自动触发render。

这些时候均可以手动调用forceUpdate自动触发render。因此建议使用immutable来操做state,redux等flux架构来管理state。

三、父组件的状态变化怎么影响子组件的生命周期?

shouldComponentUpdate函数接受nextProps和nextState做为参数。

你能够根据props里面设的值或者state里面设的值来判断需不须要更新。例如判断this.state.count是否等于nextState.count。

四、为何在react componentWillUpdate中调用setstate会形成循环调用?

若是在shouldComponentUpdate和componentWillUpdate中调用了setState,

此时this._pendingStateQueue != null,则performUpdateIfNecessary方法就会调用updateComponent方法进行组件更新。

可是updateComponent方法又会调用shouldComponentUpdate和componentWillUpdate,

所以形成循环调用,使得浏览器内存占满后崩溃。

五、函数式组件有没有生命周期?为何?

由于函数式组件没有继承React.Component,因为生命周期函数是React.Component类的方法实现的

因此没继承这个类,天然就无法使用生命周期函数了

六、PureComponent和Component有什么区别?

PureComponent优点:

      不须要开发者本身实现shouldComponentUpdate,就能够进行简单的判断来提高性能。

   PureComponent缺点:

      React.PureComponent 经过props和state的浅对比来实现 shouldComponentUpate()。

在PureComponent中,若是包含比较复杂的数据结构,可能会因深层的数据不一致而产生错误的否认判断,致使界面得不到更新。

PureComponent 在使用 shouldComponentUpdate的处理是由 PureComponent 自身来处理,而不是由用户来控制,

因此咱们在 PureComponent 中若是复写今生命周期回调函数,React 会提示咱们错误的。告诉咱们不容许重写该方法。

七、为何PureComponent比较复杂的数据结构,可能会因深层的数据不一致而产生错误的否认判断?

JavaScript 中的对象通常是可变的(Mutable),由于使用了引用赋值,新的对象简单的引用了原始对象,

改变新的对象将影响到原始对象。

foo={a: 1};

  bar=foo;

  bar.a=2

你会发现此时 foo.a 也被改为了 2。

为了解决这个问题,通常的作法是使用 shallowCopy(浅拷贝)或 deepCopy(深拷贝)来避免被修改,但这样作形成了 CPU 和内存的浪费。

缘由在于js使用的是引用赋值,新的对象简单引用了原始对象,改变新对象虽然影响了原始对象,

但对象的地址仍是同样,使用===比较的方式相等。

而在PureComponent中,会被断定prop相等而不触发render()。

避免此类问题最简单的方式是,避免使用值可能会突变的属性或状态,而是使用副原本返回新的变量

八、react中的setstate是同步的仍是异步的?Rax呢?

由React控制的事件处理程序,以及生命周期函数调用setState不会同步更新state 。

React控制以外的事件中调用setState是同步更新的。好比原生js绑定的事件,setTimeout/setInterval等。

大部分开发中用到的都是React封装的事件,好比onChange、onClick、onTouchMove等,这些事件处理程序中的setState都是异步处理的。

Rax的setState是同步的

九、React是怎样控制异步和同步的?

在 React 的 setState 函数实现中,会根据一个变量 isBatchingUpdates

判断是直接更新 this.state 仍是放到队列中延时更新,

而 isBatchingUpdates 默认是 false,表示 setState 会同步更新 this.state;

可是,有一个函数 batchedUpdates,该函数会把 isBatchingUpdates 修改成 true,

而当 React 在调用事件处理函数以前就会先调用这个 batchedUpdates将isBatchingUpdates修改成true,

这样由 React 控制的事件处理过程 setState 不会同步更新 this.state。

十、setState的一些用法?

(1)多个setState调用会合并处理:

在某些处理程序中调用了两次setState,可是render只执行了一次。

由于React会将多个this.setState产生的修改放在一个队列里进行批延时处理。

(2)参数为函数的setState用法

假如setState更新state后我但愿作一些事情,

而setState多是异步的,那我怎么知道它何时执行完成。

因此setState提供了函数式用法

接收两个函数参数,第一个函数调用更新state,第二个函数是更新完以后的回调。

case:

handleClick() {
this.setState({

count: this.state.count + 1

})
}
以上操做存在潜在的陷阱,不该该依靠它们的值来计算下一个状态。

handleClick() {
this.setState({

count: this.state.count + 1

})
this.setState({

count: this.state.count + 1

})
this.setState({

count: this.state.count + 1

})
}
最终的结果只加了1
由于调用this.setState时,并无当即更改this.state,
因此this.setState只是在反复设置同一个值而已,上面的代码等同于这样

handleClick() {
const count = this.state.count

this.setState({

count: count + 1

})
this.setState({

count: count + 1

})
this.setState({

count: count + 1

})
}
count至关于一个快照,因此无论重复多少次,结果都是加1。

第一个函数接收先前的状态做为第一个参数,将这次更新被应用时的props作为第二个参数。

increment(state, props) {
return {

count: state.count + 1

}
}

handleClick() {
this.setState(this.increment)
this.setState(this.increment)
this.setState(this.increment)
}
对于屡次调用函数式setState的状况,React会保证调用每次increment时,state都已经合并了以前的状态修改结果。

也就是说,第一次调用this.setState(increment),

传给increment的state参数的count是10,第二调用是11,第三次调用是12,最终handleClick执行完成后的结果就是this.state.count变成了13。

值得注意的是:在increment函数被调用时,this.state并无被改变,

依然要等到render函数被从新执行时(或者shouldComponentUpdate函数返回false以后)才被改变,

由于render只执行一次。

相关文章
相关标签/搜索