React Component Lifecycle

概述

我们先来理一理React的生命周期方法有哪些:

componentWillMount

渲染前调用一次,这个时候DOM结构还没有渲染。

componentDidMount

渲染完成后调用一次,这个时候DOM结构已经渲染了。这个时候就可以初始化其他框架的设置了,如果利用jQuery绑定事件等等。

componentWillReceiveProps

初始化渲染不会调用,在接收到新的props时,会调用这个方法。

shouldComponentUpdate

初始化渲染不会调用,接收到新的props或state时调用。

componentWillUpdate

初始化渲染不会调用,更新前调用。

componentDidUpdate

初始化渲染不会调用,更新后调用。

componentWillUnmount

组件移除前调用。
根据执行的时机,这些方法可以分为三类。

组件挂载

组件渲染前后会执行,而且只会执行一次,看个例子

var A = React.createClass({      
    componentWillMount: function () {            
        console.log('A componentWillMount');
    },componentDidMount: function () {
        console.log('A componentDidMount');
    },render: function () {
        console.log('A render');
        return null;
    }
});
React.render(<A />,document.getElementById('example'));

#控制台打印
A componentWillMount
A render
A componentDidMount

componentWillMount

componentWillMount里允许我们初始化前最后一次对state进行修改,而不会触发重新渲染。

var A = React.createClass({
    getInitialState: function () {
        return {init: false};
    },componentWillMount: function () {
        this.setState({init: true});
        console.log('A componentWillMount');
    },render: function () {
        console.log('A render:' + this.state.init);
        return null;
    }
});
React.render(<A />,document.getElementById('example'));

#控制台打印
A componentWillMount
A render:true
A componentDidMount

如果在componentDidMount中setState,结果就会是这样的。

var A = React.createClass({
    getInitialState: function () {
        return {init: false};
    },componentWillMount: function () {
        console.log('A componentWillMount');
    },componentDidMount: function () {
        this.setState({init: true});
        console.log('A componentDidMount');
    },document.getElementById('example'));

#控制台打印
A componentWillMount
A render:false
A componentDidMount
A render:true

也许会有人会问了:在这个方法中

componentDidMount: function () {
    this.setState({init: true});
    console.log('A componentDidMount');
}

先调用了setState,为啥不是先打印 ‘A render:true’后打印‘A componentDidMount’呢?

setState并不是一个同步的方法,可以理解为异步。

这里容易犯的错误就是,setState完后,马上就获取state的值做处理,结果获取的还是老的state。

var A = React.createClass({
    getInitialState: function () {
        return {init: false};
    },componentDidMount: function () {
        this.setState({init: true});
        console.log('A componentDidMount:' + this.state.init);
    },document.getElementById('example'));

#控制台打印
A componentWillMount
A render:false
A componentDidMount:false
A render:true

如果想setState后获取到更新的值,可以放在回调里

var A = React.createClass({
    getInitialState: function () {
        return {init: false};
    },componentDidMount: function () {
        this.setState({init: true},function () {
            console.log('callback:' + this.state.init);
        });
        console.log('A componentDidMount');
    },document.getElementById('example'));

#控制台打印
A componentWillMount
A render:false
A componentDidMount
A render:true
callback:true

componentDidMount

componentDidMount渲染完成后执行一次,一般我们会在这里异步获取数据,重新渲染页面。例如

var A = React.createClass({
    getInitialState: function () {
        return {data: []};
    },fetchData: function (callback) {
        setTimeout(
            function () {
                callback([1,2,3]);
            },1000
        );
    },componentDidMount: function () {
        this.fetchData(function (data) {
            this.setState({data: data});
        }.bind(this));
    },render: function () {
        var data = this.state.data;
        return (
            data.length ?
                <ul>
                    {this.state.data.map(function (item) {
                        return <li>{item}</li>
                    })}
                </ul>
                :
                <div>loading data...</div>
        )
    }
});
React.render(<A />,document.getElementById('example'));

官方文档上也说的很清楚,建议我们在componentDidMount中添加ajax,因为这是DOM已经完成了初始化的渲染,在componentWillMount中获取也可以,例如上面的例子,换在componentWillMount中获取数据,完全OK的。但是不建议大家这么干,第一个是官方不推荐,另一个因为DOM还没有渲染,这个时候的一些DOM操作就会出错!

嵌套

看个父子组件的执行过程,加深对初始化渲染过程的理解。

var Child = React.createClass({
    componentWillMount: function () {
        console.log('Child componentWillMount');
    },componentDidMount: function () {
        console.log('Child componentDidMount');
    },render: function () {
        console.log('Child render');
        return null;
    }
});

var Parent = React.createClass({
    componentWillMount: function () {
        console.log('Parent componentWillMount');
    },componentDidMount: function () {
        console.log('Parent componentDidMount');
    },render: function () {
        console.log('Parent render');
        return <Child />;
    }
});
React.render(<Parent />,document.getElementById('example'));

#控制台打印
Parent componentWillMount
Parent render
Child componentWillMount
Child render
Child componentDidMount
Parent componentDidMount

组件更新

更新方法只会在组件初始化渲染完成后且触发了重新渲染的条件才会执行。更新方法同挂载方法分处组件生命周期的不同的阶段。例如一个婴儿在出生前和出生后,这是两个不同的阶段。

componentWillReceiveProps

组件接收到新的props时会调用,一般在组件嵌套中比较常见,单一组件state变化是不会执行这个函数的。例如

var A= React.createClass({
    componentWillReceiveProps: function (nextProps) {
        console.log('A componentWillReceiveProps');
    },componentDidMount: function () {
        this.setState({name: 'zzz'});
    },render: function () {
        return null;
    }
});
React.render(<A/>,document.getElementById('example'));

控制台啥也没打印

因为对组件来说,他的props是不可变的。在看另外一个例子:

var Child = React.createClass({
    componentWillReceiveProps: function (nextProps) {
        console.log('Child componentWillReceiveProps');
    },render: function () {
        return <div>{this.props.name}</div>;
    }
});

var Parent = React.createClass({
    getInitialState: function () {
        return {name: 'xxx'};
    },render: function () {
        return <Child name={this.state.name}/>;
    }
});
React.render(<Parent />,document.getElementById('example'));

#控制台打印
Child componentWillReceiveProps

尽管没有传递属性,但是方法依旧会执行,只不过nextProps是个空对象而已。有人会问了,在Child组件当中,初始化渲染的时候name值为‘xxx’,第二次更新的时候name值为‘zzz’,为什么会说组件的props是不变的呢?这里不是发生变化了么?

按照我的个人理解,组件props不变指的是在它的生命周期的阶段中,保持不变。例如初始化渲染的过程中,如果在componentWillMount方法中,手动修改props,控制台就会提示如下警告。组件更新方法主要是相应state的变化,此处更不应该去修改props。

Warning: Don't set .props.name of the React component <Child />. 
Instead,specify the correct value when initially         
creating the element. The element was created by Parent.

componentWillReceiveProps主要是在更新前,最后一次修改state,而不会触发重新渲染。有点类似componentWillMount,但是执行的时间不一样,例如

var Child = React.createClass({
    getInitialState: function () {
        return {show: false};
    },componentWillReceiveProps: function (nextProps) {
        if (this.props.name !== nextProps.name) {
            this.setState({show: true});
        }
    },render: function () {
        return this.state.show ? <div>{this.props.name}</div> : null;
    }
});

var Parent = React.createClass({
    getInitialState: function () {
        return {name: 'xxx'};
    },componentDidMount: function () {
        this.setState({name: 'xxx'});
    },document.getElementById('example'));

我们要尽量避免父子组件当中都有state,这样组件的复用性就会降低,一般来说保持最外层的容器组件同服务器、用户交互,改变state,而子组件只负责通过props接收数据,然后渲染页面。这也是官方推荐的做法。

shouldComponentUpdate

更新前调用,返回值决定了组件是否更新。例如

var A = React.createClass({
    componentDidMount: function () {
        this.setState({});
    },shouldComponentUpdate: function (nextProps,nextState) {
        console.log('A shouldComponentUpdate');
        return true;
    },componentWillUpdate: function () {
        console.log('A componentWillUpdate');
    },componentDidUpdate: function () {
        console.log('A componentDidUpdate');
    },render: function () {
        console.log('A render');
        return null ;
    }
});

React.render(<A />,document.getElementById('example'));

#控制台打印     
A render
A shouldComponentUpdate
A componentWillUpdate
A render
A componentDidUpdate

第一个render是初始化。组件会将render方法的返回值同已有的DOM结构比较,只更新有变动的的部分,这个过程是需要花费时间的,在这个方法中我可以决定是否需要更新组件,从而减少性能的损耗。

this.forceUpdate()不会执行shouldComponentUpdate方法,因为是强制更新,不会因为shouldComponentUpdate的返回值决定是否更新,所以跳过该方法。另外还需要注意的是,this.forceUpdate()调用会导致该组件的shouldComponentUpdate不执行,对子组件的shouldComponentUpdate方法没有影响。

componentWillUpdate、componentDidUpdate

组件更新前后执行,没办法决定组件是否更新,只能进行些非状态的操作,个人感觉用途不太明显。

组件更新的整个过程

var Child = React.createClass({
    componentWillReceiveProps: function () {
       console.log('Child componentWillReceiveProps');
    },nextState) {
        console.log('Child shouldComponentUpdate');
        return true;
    },componentWillUpdate: function () {
        console.log('Child componentWillUpdate');
    },componentDidUpdate: function () {
        console.log('Child componentDidUpdate');
    },render: function () {
        console.log('Child render');
        return null ;
    }
});

var Parent = React.createClass({
    componentDidMount: function () {
        this.setState({});
    },render: function () {
        return <Child />;
    }
});

React.render(<Parent />,document.getElementById('example'));

#控制台打印
Child render
Child componentWillReceiveProps
Child shouldComponentUpdate
Child componentWillUpdate
Child render
Child componentDidUpdate

第一个render是初始化调用的,不是更新的过程。

移除

componentWillUnmount

组件被移除前调用,这里可以做一些清除工作,例如清除内存,解除事件的监听等等。

var A = React.createClass({
    componentDidMount: function () {
        this.interval = setInterval(
            function () {
                console.log('running');
            },100
        );
    },handleClick: function () {
      React.unmountComponentAtNode(document.getElementById('example'));
    },componentWillUnmount: function () {
        clearInterval(this.interval);
    },render: function () {
        return <button onClick={this.handleClick}>click</button>;
    }
});

React.render(<A />,document.getElementById('example'));

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


react 中的高阶组件主要是对于 hooks 之前的类组件来说的,如果组件之中有复用的代码,需要重新创建一个父类,父类中存储公共代码,返回子类,同时把公用属性...
我们上一节了解了组件的更新机制,但是只是停留在表层上,例如我们的 setState 函数式同步执行的,我们的事件处理直接绑定在了 dom 元素上,这些都跟 re...
我们上一节了解了 react 的虚拟 dom 的格式,如何把虚拟 dom 转为真实 dom 进行挂载。其实函数是组件和类组件也是在这个基础上包裹了一层,一个是调...
react 本身提供了克隆组件的方法,但是平时开发中可能很少使用,可能是不了解。我公司的项目就没有使用,但是在很多三方库中都有使用。本小节我们来学习下如果使用该...
mobx 是一个简单可扩展的状态管理库,中文官网链接。小编在接触 react 就一直使用 mobx 库,上手简单不复杂。
我们在平常的开发中不可避免的会有很多列表渲染逻辑,在 pc 端可以使用分页进行渲染数限制,在移动端可以使用下拉加载更多。但是对于大量的列表渲染,特别像有实时数据...
本小节开始前,我们先答复下一个同学的问题。上一小节发布后,有小伙伴后台来信问到:‘小编你只讲了类组件中怎么使用 ref,那在函数式组件中怎么使用呢?’。确实我们...
上一小节我们了解了固定高度的滚动列表实现,因为是固定高度所以容器总高度和每个元素的 size、offset 很容易得到,这种场景也适合我们常见的大部分场景,例如...
上一小节我们处理了 setState 的批量更新机制,但是我们有两个遗漏点,一个是源码中的 setState 可以传入函数,同时 setState 可以传入第二...
我们知道 react 进行页面渲染或者刷新的时候,会从根节点到子节点全部执行一遍,即使子组件中没有状态的改变,也会执行。这就造成了性能不必要的浪费。之前我们了解...
在平时工作中的某些场景下,你可能想在整个组件树中传递数据,但却不想手动地通过 props 属性在每一层传递属性,contextAPI 应用而生。
楼主最近入职新单位了,恰好新单位使用的技术栈是 react,因为之前一直进行的是 vue2/vue3 和小程序开发,对于这些技术栈实现机制也有一些了解,最少面试...
我们上一节了了解了函数式组件和类组件的处理方式,本质就是处理基于 babel 处理后的 type 类型,最后还是要处理虚拟 dom。本小节我们学习下组件的更新机...
前面几节我们学习了解了 react 的渲染机制和生命周期,本节我们正式进入基本面试必考的核心地带 -- diff 算法,了解如何优化和复用 dom 操作的,还有...
我们在之前已经学习过 react 生命周期,但是在 16 版本中 will 类的生命周期进行了废除,虽然依然可以用,但是需要加上 UNSAFE 开头,表示是不安...
上一小节我们学习了 react 中类组件的优化方式,对于 hooks 为主流的函数式编程,react 也提供了优化方式 memo 方法,本小节我们来了解下它的用...
开源不易,感谢你的支持,❤ star me if you like concent ^_^
hel-micro,模块联邦sdk化,免构建、热更新、工具链无关的微模块方案 ,欢迎关注与了解
本文主题围绕concent的setup和react的五把钩子来展开,既然提到了setup就离不开composition api这个关键词,准确的说setup是由...
ReactsetState的执行是异步还是同步官方文档是这么说的setState()doesnotalwaysimmediatelyupdatethecomponent.Itmaybatchordefertheupdateuntillater.Thismakesreadingthis.staterightaftercallingsetState()apotentialpitfall.Instead,usecom