React-生命周期

生命周期

53884612-fa2b0800-4056-11e9-879f-050e0b33e0e9.png?ynotemdtimestamp=1551878671356

初始化阶段

render

只能访问this.props和this.state,不允许修改状态和DOM输出

componentWillMount

render之前最后一次修改状态的机会

在ssr中 这个方法将会被多次调用,所以会重复触发多少遍,同时在这里如果绑定事件,将无法解绑,导致内存泄漏,变得不够安全高效逐步废弃。

因此需要加上UNSAFE

UNSAFE_componentWillMount () {

}

componentDidMount

成功render并渲染完成真实DOM之后触发,可以修改DOM

componentWillMount () {

}

运行中阶段

componentWillReceiveProps

外部组件多次频繁更新传入多次不同的props,会导致不必要的异步请求

触发

UNSAFE_componentWillReceiveProps(nextProps) 在组件接收到新的参数时被触发,当父组件导致子组件更新的时候, 即使接收的 props 并没有变化, 这个函数也会被调用.

工作方式

参数是组件接收到的新的 props , 用于比对新的 props 和原有的 props, 用户需要在函数体中调用 setState() 来更新组件的数据.

UNSAFE_componentWillReceiveProps(nextProps){
    if (this.props.currentExercise.id !== nextProps.currentExercise.id){
        this.setState({...nextProps.currentExercise})
    }
}

shouldComponentUpdate

  1. setState()函数在任何情况下都会导致组件重渲染吗?如果setState()中参数还是原来没有发生任何变化的state呢?

  2. 如果组件的state没有变化,并且从父组件接受的props也没有变化,那它就一定不会重渲染吗?

  3. 如果1,2两种情况下都会导致重渲染,我们该如何避免这种冗余的操作,从而优化性能?

没有导致state的值发生变化的setState是否会导致重渲染 ——【会!】

shouldComponentUpdate是一个性能调优函数,是重渲染时render()函数调用前被调用的函数,它接受两个参数:nextProps和nextState,分别表示下一个props和下一个state的值。并且,当函数返回false时候,阻止接下来的render()函数的调用,阻止组件重渲染,而返回true时,组件照常重渲染。

nextProps:修改后的属性

nextState:修改后的状态

可以根据状态和参数中的状态的对比,返回true/false来判断是否需要更新

shouldComponentUpdate (nextProps,nextState) {
    return !(this.state.myname === nextState.myname) ||!(this.state.myage === nextState.myage) 
}

componentWillUpdate

更新前记录DOM 状态, 可能会做一些处理,与componentDidUpdate相隔时间如果过长,会导致状态不太信

import React, { Component } from 'react'

export default class App extends Component {
  state= {
    myname:'retr0'
  }
  UNSAFE_componentWillUpdate () {
    console.log('componentWillUpdate');
  }
  render() {
    return (
      <div>
        {this.state.myname}
        <button onClick={()=>{
          this.setState({
            myname:'xiaoming'
          })
        }}>Click</button>
      </div>
    )
  }
}

componentDidUpdate

import React, { Component } from 'react'

export default class App extends Component {
  state= {
    myname:'retr0'
  }
  componentDidUpdate () {
    console.log('componentDidUpdate');
  }
  render() {
    return (
      <div>
        {this.state.myname}
        <button onClick={()=>{
          this.setState({
            myname:'xiaoming'
          })
        }}>Click</button>
      </div>
    )
  }
}

销毁阶段

componentWillUnMount

用于清除计时器,window.scroll=null等操作

import React, { Component } from 'react'
class Navar extends Component {
  render () {
    return (
      <div style={{background:'#f99'}}>
         navbar-<button onClick={this.handleClick}>Click</button>
      </div>
    )
  }
  handleClick = ()=>{
    this.props.onEvent();
  }
}
class Slidebar extends Component {
  render () {
    return (
      <div style={{background:'#99f'}}>
        slidebar
        <ul>
          <li>11111</li>
          <li>22222</li>
          <li>33333</li>
        </ul>
      </div>
    )
  }
  //销毁声明周期
  componentWillUnmount () {
    console.log('componentWillUnmount');
  }
}
export default class 子传父 extends Component {
  state = {
    isShow:true
  }
  render() {
    return (
      <div>
        App
        <Navar onEvent={()=>{
          this.setState({
            isShow : !this.state.isShow
          })
        }}/>
        {this.state.isShow?<Slidebar/>:''}
      </div>
    )
  }
}

新增

static getDerivedStateFromProps

getDerivedStateFromProps 是一个静态方法, 是一个和组件自身”不相关”的角色. 在这个静态方法中, 除了两个默认的位置参数 nextProps 和 currentState 以外, 你无法访问任何组件上的数据。第一次的初始化组件以及后续的更新过程中(包括自身状态更新以及父传子) ,返回一个对象作为新的state,返回null则说明不需要在这里更新state

会被频繁地触发

无论是组件调用了 setState(), 接收的 props 发生了变化, 或是父组件的更新都会导致子组件上的 getDerivedStateFromProps被触发.

使用的时候必须非常小心

由于 getDerivedStateFromProps 会在 setState() 后被调用, 并且它的返回值会被用于更新数据. 这意味着你会在 setState() 之后触发 getDerivedStateFromProps, 然后可能意外地再次 “setState()”.

getDerivedStateFromProps(nextProps) 函数中的第一个位置参数未必是 “新” 的 props. 在组件内调用了 setState() 时, getDerivedStateFromProps 会被调用. 但是此时的组件其实并没有获得 “新” 的 props, 是的, 这个 nextProps 的值和原来的 props 是一样的.

这就导致了我们在使用 getDerivedStateFromProps 时, 必须添加很多逻辑判断语句来处理 props 上的更新和 state 上的更新, 避免意外地返回了一个 Updater 再次更新数据, 导致数据异常.

import React, { Component } from 'react'

class Child extends Component {
  state={
    ajax:''
  }
  // UNSAFE_componentWillReceiveProps () {
  //   console.log('componentWillReceiveProps');
  // }
  componentDidUpdate () {
    console.log(this.state.ajax);
  }
  static getDerivedStateFromProps (nextProps,currentState){
    return {
      ajax:nextProps.ajax
    }
  }
  render () {
    return (
      <div>
        child - {this.props.ajax}
      </div>
    )
  }
}

export default class App extends Component {
  state= {
    ajax:9000
  }
  render() {
    return (
      <div>
        <Child ajax={this.state.ajax}/>
        <button onClick={()=>{
          this.setState({
            ajax:1000
          })
        }}>Click</button>
      </div>
    )
  }
}

更优雅的做法

React 官方博客中提供了以下几种方案:

  1. 让表单控件变成完全受控组件, 不论是 onChange 处理函数还是 value 都由父组件控制, 这样用户无需再考虑这个组件 props 的变化和 state 的更新.
function EmailInput(props) {
  return <input onChange={props.onChange} value={props.email} />;
}
  1. 让表单控件变成完全不受控组件, 但是具有 key 属性.
    仍然用自身的数据来控制 value. 但是接收 props 中的某个字段作为 key 属性的值, 以此响应 props 的更新: 当 key 的值变化时 React 会替换 (reset)组件, 从而重新生成初始化数据.

When a key changes, React will create a new component instance rather than update the current one.

示例代码:

//组件内的代码
class EmailInput extends Component {
  state = { email: this.props.defaultEmail };

  handleChange = event => {
    this.setState({ email: event.target.value });
  };

  render() {
    return <input onChange={this.handleChange} value={this.state.email} />;
  }
}
// 在父组件中接收 props 中的数据作为 key
<EmailInput
  defaultEmail={this.props.user.email}
  key={this.props.user.id}
/>
  • 其他方法请参考 这里

getSnapshotBeforeUpdate

取代了 componetWillUpdate ,触发时间为update发生的时候,在render之后,dom渲染之前返回一个值,作为componentDidUpdate的第三个参数。

import React, { Component } from 'react'

export default class App extends Component {
  state = {
    mytext :'retr0',
  }
  // componentWillUpdate () {
  //   console.log(this.state.mytext);
  // }
  getSnapshotBeforeUpdate () {
    console.log(this.state.mytext,'获取滚动条的位置');
    return {
      y:100
    }
  }
  componentDidUpdate(prevProps, prevState,data) {
    console.log(prevProps,prevState,data);
  }
  
  render() {
    console.log('render');
    return (
      <div>
        {this.state.mytext}
        <button onClick={()=>{
          this.setState({
            mytext:'xiaoming'
          })
        }}>Click</button>
      </div>
    )
  }
}