自我成长——React生命周期函数

887 阅读5分钟

自我成长——React生命周期函数

生命周期.png

在React的类组件中,官方为其提供了生命周期的概念组件的生命周期大抵分为三个阶段:挂载(mount)、更新(update)、卸载(unmount)。

挂载阶段:在组件初始化中执行且只执行一次

更新阶段:当props和state改变时触发零次或多次

卸载阶段:在组件销毁时执行且只执行一次

挂载阶段中涉及到的生命周期

1、constructor 

通过给this.state赋值对象来初始化内部state

将事件处理程序方法绑定到实例。


constructor(props) {

  super(props);

  this.state = { name:sans };

  this.initData = this.initData.bind(this);

}

ps:不要把props直接赋值给state,这样操作state不会随着props更新而变化。

在constructor中不要调用setState方法,直接给this.state赋值

2、static getDerivedStateFromProps(props, state)

getDerivedStateFromProps是一个静态方法,在挂载和更新阶段时调用,可以返回一个对象来更新状态或者返回null不更新。

可以通过此生命周期函数比较新的props和之前的state中的数据,查看是否变化,再去更新state。也可以不判断直接更新state值。


static getDetivedStateFromProps(props,state){

  const { name } = props;

  // 判断新的props和之前的state是否相同,如果改变就相应更新state

  if( name != state.name) {

    return {

      name

    };

  }

  //否则不处理

  return null;

}

3、render

render是类组件中唯一必须实现的方法,在此方法中会检查props和state的变化并渲染react组件。


render(){

  return(

  <div>

    <h1>Hello World</h1>

    </div>

  )

}

4、componentDidMount

此方法在组件挂载完成后调用,适用于一些异步数据请求、设置定时器、dom节点初始化的操作,只会调用一次。

ps:1、如果在此方法中设置了setInterval、setTimeout记得在componentWillUnmount里清除掉!

2、在此方法可以直接调用setState,官网解释如下:

你可以在 componentDidMount() 直接调用 setState() 。它将触发额外渲染,但此渲染会发生在浏览器更新屏幕之前。如此保证了即使在 render() 两次调用的情况下,用户也不会看到中间状态。请谨慎使用该模式,因为它会导致性能问题。通常,你应该在 constructor() 中初始化 state。如果你的渲染依赖于 DOM 节点的大小或位置,比如实现 modals 和 tooltips 等情况下,你可以使用此方式处理


componentDidMount(){

  //设置定时器

  this.timer = setTimeout(() => {

        console.log('component did mount')

      }, 300);

  //获取异步数据

  this.initData();

}

更新阶段涉及到的生命周期

1、getDerivedStateFromProps

2、shouldComponentUpdate(nextProps, nextState)

在react很多场景下会造成react不必要的重新渲染,比如setState但是state的值其实没有变化,组件还是会rerender,而此时就会导致不必要的性能损耗。在这种场景下,就可以通过shouldComponentUpdate生命周期去优化代码。

该方法主要是将当前的props与nextProps比较,当前的state和nextState比较,如果返回false则可以跳过更新,但是该方法并不会阻止子组件在state中渲染,所以如果想在shouldComponentUpdate去阻止render,可能会产出bug。


shouldComponentUpdate(nextProps,nextState){

  //如果值没有变化,则返回false,跳过更新

      if(nextProps.name == this.props.name){

        //当返回false时,后续不会调用componentWIllUpdate、render、componentDidUpdate这几个函数

        //但是仍可能重新渲染

        return false

      }

      return true

  }

ps:在该生命周期中进行的是浅比较,并且不建议在其中进行深层比较或者使用JSON.stringfy(),会非常影响效率且损害性能

3、render

当组件的props、state变更时,或者强制更新时都会触发render方法,并且render函数是一个纯函数,在class组件中必须实现。

4、getSnapShotBeforeUpdate(prevProps, prevState)

这个生命周期也是react16.4之后新出的,会在最近的一次渲染之前调用,当我们想要获取render之前的dom状态时就可以使用该方法,并且该生命周期的任何返回值将变成参数传给componentDidUpdate。

看看官网的🌰:





  getSnapshotBeforeUpdate(prevProps, prevState) {

    // 如果list添加了新的item,就在最新一次render前获取当前list的高度

    if (prevProps.list.length < this.props.list.length) {

      const list = this.listRef.current;

      return list.scrollHeight - list.scrollTop;

    }

    return null;

  }




  componentDidUpdate(prevProps, prevState, snapshot) {

      // 如果我们 snapshot 有值,说明我们刚刚添加了新的 items,

      // 调整滚动位置使得这些新 items 不会将旧的 items 推出视图。

      //(这里的 snapshot 是 getSnapshotBeforeUpdate 的返回值)

      if (snapshot !== null) {

        const list = this.listRef.current;

        list.scrollTop = list.scrollHeight - snapshot;

      }

    }

5、componentDidUpdate(prevProps,prevState,snapshot)

该方法会在组件更新结束后立即执行,组件首次渲染后不会执行此方法。


componentDidUpdate(prevState){

  // 如果更新后state发生了变化,就去获取data

  if(this.state.name !== prevState.name) {

    this.getData();

  }

}

ps:如果要在该方法中执行setState操作,必须先给一层条件判断,否则会导致死循环,还会导致额外的重新渲染,影响组件性能。

卸载阶段涉及到的生命周期

1、componetWillUnmount

在组件卸载之前,会调用该生命周期。一般我们都会在该方法里去做一些清除一些事件的监听、定时器的清理或取消网络请求等操作。


componentWillUnmount(){

  // 清除定时器

  clearTimeout(this.timer);

}

ps:在这里调用setState没啥用,组件马上就销毁了不会再重新渲染。

React新旧生命周期的对比

综上所述,在新生命周期中,有三个生命周期方法即将过时,要避免使用并且使用前要加上UNSAFE_前缀

并且添加了两个新的生命周期函数

总结:

在使用生命周期时,还要首先把握每一个方法的使用场景,避免造成一些不必要的性能损耗和死循环(如在componentDidUpdate中直接setState),只有了解了生命周期的顺序和各方法作用,才能写出更好地代码,冲冲冲。