React 从 v16 开始,像是跨入了新的时代,性能和新的 API 都使人瞩目。从新认识 React,从从新认识生命周期开始。为了更好的支持异步渲染(Async Rendering),解决一些生命周期滥用可能致使的问题,React 从 V16.3
开始,对生命周期进行渐进式调整,同时在官方文档也提供了使用的最佳实践。前端这里咱们将简要对比 React 新旧生命周期,从新认识一下 React 生命周期。java
getDerivedStateFromProps()
getSnapshotBeforeUpdate()
react
componentWillMount()
componentWillReceiveProps()
componentWillUpdate()
ajax
虽然废弃了这三个生命周期方法,可是为了向下兼容,将会作渐进式调整。(详情见#12028)
V16.3 并未删除这三个生命周期,同时还为它们新增以 UNSAFE_
前缀为别名的三个函数 UNSAFE_componentWillMount()
、UNSAFE_componentWillReceiveProps()
、UNSAFE_componentWillUpdate()
。数组
在 16.4 版本给出警告将会弃用 componentWillMount()
、componentWillReceiveProps()
、componentWillUpdate()
三个函数服务器
而后在 17 版本将会删除 componentWillMount()
、componentWillReceiveProps()
、componentWillUpdate()
这三个函数,会保留使用 UNSAFE_componentWillMount()
、UNSAFE_componentWillReceiveProps()
、UNSAFE_componentWillUpdate()
网络
图示数据结构
react的生命周期大概分为框架
不能进行修改state的操做
,即便作了,也不会进行新数据状态的渲染。在该函数中作的操做,均可以提早到构造函数中。渲染函数
,惟一的必定不能省略的函数,必须有返回值,返回null或false表示不渲染任何DOM元素。它是一个仅仅用于渲染的纯函数,返回值彻底取决于this.state和this.props,不能在函数中任何修改props、state、拉取数据等具备反作用的操做。render函数返回的是JSX的对象,该函数并不由于这渲染到DOM树,什么时候进行真正的渲染是有React库决定的。(setState是一个异步函数)挂载成功函数
。该函数不会再render函数调用完成以后当即调用,由于render函数仅仅是返回了JSX的对象,并无当即挂载到DOM树上,而componentDidMount是在组件被渲染到DOM树以后被调用的。另外,componentDidMount函数在进行服务器端渲染时不会被调用。当组件挂载到DOM树上以后,props/state
被修改会致使组件进行更新操做。更新过程会以此调用以下的生命周期函数:dom
卸载过程只涉及一个函数componentWillUnmount,当React组件要从DOM树上删除前,会调用一次这个函数。这个函数常常用于去除componentDidMount函数带来的反作用,例如清除计时器
、删除componentDidMount中创造的非React元素
。
setState
要修改state,只能使用this.setState()
,不能使用this.state.value='myData' 相似方式设置state,一是不会驱动从新渲染,二是极可能被后面的操做替换,形成没法预知的错误。此外,React利用状态队列来实现setState的异步更新,避免频繁地重复更新state
。当同时作了不少setState操做的时候,react会智能的合并成一个setState,当须要肯定的setState完成后的操做,可使用
setState({}, () => { // 在这里进行state改变后的操做 })
setState的调用是有风险的,在某些生命周期函数中调用可能会无用甚至早恒循环调用致使崩溃。state的初始化通常在构造函数中实现;setState能够在装载过程的componentWillMount、componentDidMount中调用
;setState能够在更新过程当中的componentWillReceiveProps、componentDidUpdate中调用
render
render是一个异步函数,render执行后并不会直接生成Dom,而是生成虚拟Dom节点(模拟HTML Dom节点的一个javaScript数据结构),什么时候生成真实的DOM树取决于react框架自己的计算
图示
getDerivedStateFromProps
static getDerivedStateFromProps(nextProps, prevState) { //根据nextProps和prevState计算出预期的状态改变,返回结果会被送给setState }
getSnapshotBeforeUpdate
差别
全部被删除的生命周期函数,目前还凑合着用,可是只要用了,开发模式下会有红色警告,在下一个大版本(也就是React v17)更新时会完全废弃。
static getDerivedStateFromProps(nextProps, prevState) { 4. Updating state based on props 7. Fetching external data when props change } constructor() { 1. Initializing state } componentDidMount() { 2. Fetching external data 3. Adding event listeners (or subscriptions) } shouldComponentUpdate() { } render() { } getSnapshotBeforeUpdate(prevProps, prevState) { 8. Reading DOM properties before an update } componentDidUpdate(prevProps, prevState, snapshot) { 5. Invoking external callbacks 6. Side effects on props change } componentWillUnmount() { }
// before
componentWillMount() { // 1. Initializing state // 2. Fetching external data // 3. Adding event listeners (or subscriptions) } componentWillReceiveProps() { // 4. Updating state based on props // 6. Side effects on props change // 7. Fetching external data when props change } componentWillUpdate(nextProps, nextState) { // 5. Invoking external callbacks // 8. Reading DOM properties before an update }
目前 react 16.8 +的生命周期分为三个阶段,分别是 挂载阶段、 更新阶段、 卸载阶段
挂载阶段:constructor(props)
: 实例化。static getDeriverdStateFromProps
从 props
中获取 state
。render
渲染。componentDidMount
: 完成挂载。
更新阶段:static getDeriverdStateFromProps
从 props
中获取 state
。shouldComponentUpdate
判断是否须要重绘。render
渲染。getSnapshotBeforeUpdate
获取快照。componentDidUpdate
渲染完成后回调。
卸载阶段:componentWillUnmount
即将卸载。
错误处理:static getDerivedStateFromError
从错误中获取 state。componentDidCatch
捕获错误并进行处理。
组件实例建立并插入 DOM 时,按顺序调用如下方法:
constructor()
static getDerivedStateFromProps()
componentWillMount()/UNSAFE_componentWillMount()(being deprecated)render()
componentDidMount()
有定义 getDerivedStateFromProps 时,会忽略 componentWillMount()/UNSAFE_componentWillMount()
(详情查看源码)
1)constructor()constructor(props)
构造函数一般用于:
注意:ES6 子类的构造函数必须执行一次 super()。React 若是构造函数中要使用 this.props,必须先执行 super(props)。
2)static getDerivedStateFromProps()static getDerivedStateFromProps(nextProps, prevState)
当建立时、接收新的 props 时、setState 时、forceUpdate 时会执行这个方法。
注意:v16.3 setState 时、forceUpdate 时不会执行这个方法,v16.4 修复了这个问题。
这是一个静态方法
,参数 nextProps
是新接收的 props
,prevState
是当前的 state
。返回值(对象)将用于更新 state,若是不须要更新则须要返回 null。
下面是官方文档给出的例子
class ExampleComponent extends React.Component { // Initialize state in constructor, // Or with a property initializer. state = { isScrollingDown: false, lastRow: null, }; static getDerivedStateFromProps(props, state) { if (props.currentRow !== state.lastRow) { return { isScrollingDown: props.currentRow > state.lastRow, lastRow: props.currentRow, }; } // Return null to indicate no change to state. return null; } }
这个方法的经常使用做用也很明显了:父组件传入新的 props
时,用来和当前的 state
对比,判断是否须要更新 state
。之前通常使用 componentWillReceiveProps
作这个操做。
这个方法在建议尽可能少用
,只在必要的场景中使用,通常使用场景以下:
无条件的根据 props 更新 state
当 props 和 state 的不匹配状况更新 state
详情能够参考官方文档的最佳实践 You Probably Don’t Need Derived State
3)componentWillMount()/UNSAFE_componentWillMount()(弃用)UNSAFE_componentWillMount()
这个方法已经不推荐使用。由于在将来异步渲染机制下,该方法可能会屡次调用。它所行使的功能也能够由 componentDidMount() 和 constructor() 代替:
以前有些人会把异步请求放在这个生命周期,其实大部分状况下都推荐把异步数据请求放在 componentDidMount() 中
。在服务端渲染时,一般使用 componentWillMount() 获取必要的同步数据
,可是可使用 constructor() 代替它。
可使用 setState,不会触发 re-render
4)renderrender()
每一个类组件中,render() 惟一必须的方法。
render() 正如其名,做为渲染用,能够返回下面几种类型:
注意:
Arrays 和 String 是 v16.0.0 新增。
fragments 是 v16.2.0 新增。
Portals 是 V16.0.0 新增。
里面不该该包含反作用,应该做为纯函数。
不能使用 setState。
5)componentDidMount()componentDidMount()
组件完成装载(已经插入 DOM 树)时,触发该方法。这个阶段已经获取到真实的 DOM。
通常用于下面的场景:
componentWillReceiveProps()/UNSAFE_componentWillReceiveProps()(being deprecated)static getDerivedStateFromProps()
shouldComponentUpdate()
componentWillUpdate()/UNSAFE_componentWillUpdate()(being deprecated)render()
getSnapshotBeforeUpdate()
componentDidUpdate()
有 getDerivedStateFromProps
或者 getSnapshotBeforeUpdate
时,componentWillReceiveProps()
/UNSAFE_componentWillReceiveProps()
和 componentWillUpdate()
/UNSAFE_componentWillUpdate()
不会执行 (详情查看源码)
1)componentWillReceiveProps()/UNSAFE_componentWillReceiveProps()(弃用)UNSAFE_componentWillReceiveProps(nextProps)
这个方法在接收新的 props 时触发,即便 props 没有变化也会触发。
通常用这个方法来判断 props 的先后变化来更新 state,以下面的例子:
class ExampleComponent extends React.Component { state = { isScrollingDown: false, }; componentWillReceiveProps(nextProps) { if (this.props.currentRow !== nextProps.currentRow) { this.setState({ isScrollingDown: nextProps.currentRow > this.props.currentRow, }); } } }
这个方法将被弃用,推荐使用 getDerivedStateFromProps
代替。
可使用 setState
2)static getDerivedStateFromProps()
同 Mounting 时所述一致。
3)shouldComponentUpdate()
在接收新的 props 或新的 state 时,在渲染前会触发该方法。
该方法经过返回 true 或者 false 来肯定是否须要触发新的渲染。返回 false, 则不会触发后续的 UNSAFE_componentWillUpdate()、render() 和 componentDidUpdate()(可是 state 变化仍是可能引发子组件从新渲染)。
因此一般经过这个方法对 props 和 state 作比较,从而避免一些没必要要的渲染。
PureComponent 的原理就是对 props 和 state 进行浅对比(shallow comparison),来判断是否触发渲染。
4)componentWillUpdate()/UNSAFE_componentWillUpdate() (弃用)UNSAFE_componentWillUpdate(nextProps, nextState)
当接收到新的 props 或 state 时,在渲染前执行该方法。
在之后异步渲染时,可能会出现某些组件暂缓更新,致使 componentWillUpdate 和 componentDidUpdate 之间的时间变长,这个过程当中可能发生一些变化,好比用户行为致使 DOM 发生了新的变化,这时在 componentWillUpdate 获取的信息可能就不可靠了。
不能使用 setState
5)render()
同 Mounting 时所述一致。
6)getSnapshotBeforeUpdate()getSnapShotBeforeUpdate(prevProps, prevState)
这个方法在 render() 以后,componentDidUpdate() 以前调用。
两个参数 prevProps 表示更新前的 props,prevState 表示更新前的 state。
返回值称为一个快照(snapshot),若是不须要 snapshot,则必须显示的返回 null —— 由于返回值将做为 componentDidUpdate() 的第三个参数使用。因此这个函数必需要配合 componentDidUpdate() 一块儿使用。
这个函数的做用是在真实 DOM 更新(componentDidUpdate)前,获取一些须要的信息(相似快照功能),而后做为参数传给 componentDidUpdate。例如:在 getSnapShotBeforeUpdate 中获取滚动位置,而后做为参数传给 componentDidUpdate,就能够直接在渲染真实的 DOM 时就滚动到须要的位置。
下面是官方文档给出的例子:
class ScrollingList extends React.Component { constructor(props) { super(props); this.listRef = React.createRef(); } getSnapshotBeforeUpdate(prevProps, prevState) { // Are we adding new items to the list? // Capture the scroll position so we can adjust scroll later. if (prevProps.list.length < this.props.list.length) { const list = this.listRef.current; return list.scrollHeight - list.scrollTop; } return null; } componentDidUpdate(prevProps, prevState, snapshot) { // If we have a snapshot value, we've just added new items. // Adjust scroll so these new items don't push the old ones out of view. // (snapshot here is the value returned from getSnapshotBeforeUpdate) if (snapshot !== null) { const list = this.listRef.current; list.scrollTop = list.scrollHeight - snapshot; } } render() { return ( <div ref={this.listRef}>{/* ...contents... */}</div> ); } }
7)componentDidUpdate()componentDidUpdate(prevProps, prevState, snapshot)
这个方法是在更新完成以后调用,第三个参数 snapshot 就是 getSnapshotBeforeUpdate 的返回值。
正如前面所说,有 getSnapshotBeforeUpdate 时,必需要有 componentDidUpdate。因此这个方法的一个应用场景就是上面看到的例子,配合 getSnapshotBeforeUpdate 使用。
可使用 setState,会触发 re-render,因此要注意判断,避免致使死循环。
1)componentWillUnmount()
componentWillUnmount()
在组件卸载或者销毁前调用。这个方法主要用来作一些清理工做,例如:
不能使用 setState
componentDidCatch()componentDidCatch(err, info)
任何子组件在渲染期间,生命周期方法中或者构造函数 constructor 发生错误时调用。
错误边界不会捕获下面的错误:
总结
React 生命周期能够查看 生命周期图
虽然 React 有作向下兼容,可是推荐尽可能避免使用废弃的生命周期,而是拥抱将来,用新的生命周期替换它们。
若是你不想升级 React,可是想用新的生命周期方法,也是能够的。使用 react-lifecycles-compat polyfill,能够为低版本的 React(0.14.9+)提供新的生命周期方法。
若是你以为这篇文章对你有所帮助,那就顺便点个赞
吧,点赞收藏
不迷路~
黑芝麻哇,白芝麻发,黑芝麻白芝麻哇发哈!
前端哇发哈