• React生命周期函数


      挂载阶段

      constructor -> static getDerivedStateFromProps ->  render -> componentDidMount

      constructor(构造函数):初始化状态,当然它还接受一个props参数,可以使用组件传递过来的props,通常是用props初始化state。

      getDerivedStateFromProps: 被static 修饰,静态方法,这也意味着,在函数里面不能使用this和实例方法。它接受两个参数 props 和state,在渲染之前,根据props计算得到state,并返回state。

      render(渲染):组件中render 函数,它是至关重要的,因为组件的目的就是要渲染内容到页面上,不过它返回是一个虚拟DOM, ReactDOM 根据它构建出真实的DOM渲染到页面上。它会根据props 和state 渲染出react element, 指导react渲染出真正的dom. 但总存在特殊的情况,组件不需要渲染什么东西,如验证用户有没有登录的组件,它确实不需要返回什么,我们只是根据它进行跳转到不同的页面,这时直接在render 函数中返回null.

      componentDidMount(挂载完成):当真实的DOM 渲染到页面上会触发它。 需要注意的是render 函数在调用完成后,componentDidMount 并不会立刻调用,而是在当所有组件的render函数都调用完华之后,每个组件的componentDidMount 才会调用。 写一下代码会看得比较清楚,这也是我第一次明白地认识了react的挂载生命周期。

      写一个Counter 和一个CounterWrapper 组件, CounterWrapper包含Counter 。CounterWrapper 为父组件,Counter 为子组件。为了更能清楚地明白生命周期函数,CounterWrapper组件多包含几个Counter. 为了对每一个Counter 进行区分,分别给它们传一个caption 和initValue 参数。 在CounterWrapper 中 由于没有state, 我只写了render函数和ComponentDidMount 函数,而在子组件Counter中,它有state, 有完整的生命周期函数,依次写了四个阶段,constructor, static  getDerivedStateFromProps, render, componentDidMount, 当然这里只是console.log一下信息,看一下执行顺序

    // 子组件Counter
    class Counter extends React.Component {
        constructor(props) {
            super(props);
            console.log(`进入子组件Counter的构造函数constructor ----- ${props.caption}`);
            this.state = {
                count: props.initValue || 0
            }
        }
    
        static getDerivedStateFromProps(props) {
            console.log(`进入子组件Counter的getDerivatedStateFromProps ----- ${props.caption}`);
        }
        render() {
            console.log(`进入子组件Counter的render ----- ${this.props.caption}`);
            const buttonStyle = {
                margin: '10px'
            };
            return (
                <div>
              <button style={buttonStyle} >+</button> <button style={buttonStyle} value={5}>-</button> <span>{this.props.caption}count: {this.state.count}</span> </div> ); } componentDidMount() { console.log(`进入子组件Counter的componentDidMount ----- ${this.props.caption}`); } } // 父组件CounterWrapper class CounterWrapper extends React.Component { render(){ console.log('进入父组件CounterWrapper的render 函数'); return (<div> <Counter caption="第一个" initValue={3}></Counter> <Counter caption="第二个" initValue={6}></Counter> <Counter caption="第三个" initValue={9}></Counter> </div>) } componentDidMount() { console.log('进入父组件CounterWrapper的 componentDidMount 函数'); } }

      打开控制台,重点看一下componentDidMount。先进入的是父组件CounterWrapper 的render 函数,然后再进入第一个子组件的constructor, getDerivatedStateFromProps和 render, 再进入第二个子组件的constructor, getDerivatedStateFromProps和 render函数,再进入第三个子组件的constructor, getDerivatedStateFromProps和 render函数, 最后才是每一个子组件的componentDidMount, 第二个子组件的ccomponentDidMount函数, 第三个子组件的ccomponentDidMount函数,最后的最后是父组件的CompoentDidMount.

      组件的挂载生命周期并不是依次调用的,render 函数后面并不是紧紧根随着componentDidMount 函数。只有当三个组件的render函数都调用完成后,三个组件的componentDidMount 才连在一起被调用。 只有当所有的子组件的componentDidMount都调用了,父组件的componetDidMount 才会被调用。在componentDidMount 函数的调用上,我一直存在误解,以为render 函数调用后,立即执行它,并认为父组件的componentDidMount优于子组件的componentDidMount 执行,真是太想当然了。

      现在再来想一下为什么会有这样的设计,当使用componentDidMount 的时候,我们听得最多的一句话可能就是,在这里,你可以操作真实的DOM了,换句话说,当 组件compoentDidMount的时候,真实的DOM 已经渲染完毕了,整个页面都已经渲染完成了。整个页面就意味着整个DOM树都已经构建完成了,注意这里是整个DOM树。当React进入到组件的render 函数时,它只知道当前组件长什么样子,而不会知道整个DOM树长什么样子,所以它只能接着找到第二个组件render, 因为后面有第三个子组件,它还是不知道整个DOM树长什么样子,所以它还要找到第三个组件render,  也就是说它会把所有的组件都循环一遍,直到能够构建整个DOM树,它才会渲染真实的DOM, 这也是最为省事的办法,如果它找到一个渲染一个,后面的组件再对前面的组件有影响,它要把前面的做法再来一次,效率低下。一次渲染就OK了。 渲染之后,肯定是最深层的组件先完成,只有小的完成了才能组成大的,所以最深层的组件的componentDidMount 先执行, 最外层的最后执行。这让我们想成了JS事件处理阶段,当构建DOM树的过程中,它是捕获阶段,从外面找到最里面,而在渲染真实的DOM的时候,componentDidMount的时候,它是冒泡阶段, 从最里面到最外面。

      更新阶段

      static getDerivedStateFromProps -> shouldComponentUpdate -> render -> getSnapshotBeforeUpdate -> componentDidUpdate

      getDerivedStateFromProps: 和挂载阶段用法是一致的, 它适应用一种情况:组件的状态一直受父组件props的影响, 具体看下面的实例,css 用的是bootstrap的,npm install bootstrap 或 直接cdn link。

    // 子组件DirectionDisplay
    export class DirectionDisplay extends React.Component {
        state = {
            direction: "up",
            lastValue: 0
        }
        getClasses() {
            return (this.state.direction === "up" ? "bg-success" : "bg-danger")
                + " text-white text-center p-2 m-2";
        }
        render() {
            return <h5 className={this.getClasses()}>
                {this.props.value}
            </h5>
        }
        /* 
        接受props和state(当前组件的), 返回state. 当前组件的state只有两个属性lastValue, direction, 所以在函数返回state的时候,最好也返回这两个属性
        这里要注意一点,使用if 判断,如果props没有变化的话,直接返回state,
        因为update 阶段,也会调用这个生命周期函数。
        */
        static getDerivedStateFromProps(props, state) {
            if (props.value !== state.lastValue) {
                return {
                    lastValue: props.value,
                    direction: state.lastValue > props.value ? "down" : "up"
                }
            }
            return state;
        }
    }
    
    // 父组件
    class App extends React.Component {
        state = {counter: 100}
        changeCounter = (val) => {
            this.setState({ counter: this.state.counter + val })
        }
        render() {
            return <div className="container text-center">
                <DirectionDisplay value={this.state.counter} />
                <div className="text-center">
                    <button className="btn btn-primary m-1"
                        onClick={() => this.changeCounter(-1)}>Decrease</button>
                    <button className="btn btn-primary m-1"
                        onClick={() => this.changeCounter(1)}>Increase</button>
                </div>
            </div >
        }
    }

      shouldComponentUpdate() : 组件应不应该更新,它决定了一个组件是不是需要重新渲染, 如果它返回fasle, 就表示该组件不需要更新,也就不会重新渲染,它后面的生命周期函数就不会被执行。如果返回true,表示该组件需要更新, 它后面的生命周期函数才会被执行,重新渲染。 接受两个参数nextProps和nextState, 可以判断它们是不是和当前的props 和state 一致,如果一致,就是返回true,表示更新,如果不一致,则返回false,就不更新了。 现在来写一下componentShouldUpdate

        // 如果父组件传递过来的value发生变化,才会返回true, 组件才需要演染。
        shouldComponentUpdate(nextProps, nextState) {
            return (nextProps.value!== this.props.value);
        }

      render 就是渲染组件了,这没有什么可说的。getSnapshotBeforeUpdate,在更新之前得到快照,在使用refs的时候会用到,一般也不会用到。  componentDidUpdate, 和componentDidMount 用法一致

      卸载阶段,

      componentwillUnmount 函数,当React 组件要从DOM树上删掉之前,对应的componentWillUnmount函数就会被调用,它适合做一些清理性的工作,就是删除这个组件之前有没有什么要清理的。注意它没有对应的did函数,因为组件删除以后,没有什么事情可以做了。

  • 相关阅读:
    codeforces628D. Magic Numbers (数位dp)
    hdu5432Rikka with Array (数位dp+十进制转化为二进制)
    LA3902 Network (树上dfs)
    zoj3494 BCD Code(AC自动机+数位dp)
    codeforces #345 (Div. 1) D. Zip-line (线段树+最长上升子序列)
    zjnu1786 PROSJEK(二分)
    九度1497:面积最大的全1子矩阵 (单调队列,单调栈)
    zjnu1726 STOGOVI (lca)
    zjnu1735BOB (单调队列,单调栈)
    uva10859 Placing Lampposts (树形dp+求两者最小值方法)
  • 原文地址:https://www.cnblogs.com/SamWeb/p/8642857.html
Copyright © 2020-2023  润新知