资讯专栏INFORMATION COLUMN

React Component Lifecycle

alphahans / 3417人阅读

摘要:例如一个婴儿在出生前和出生后,这是两个不同的阶段。主要是在更新前,最后一次修改,而不会触发重新渲染。组件更新的整个过程控制台打印第一个是初始化调用的,不是更新的过程。

概述

我们先来理一理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(, 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");
    },
    componentDidMount: function () {
        console.log("A componentDidMount");
    },
    render: function () {
        console.log("A render:" + this.state.init);
        return null;
    }
});
React.render(, 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");
    },
    render: function () {
        console.log("A render:" + this.state.init);
        return null;
    }
});
React.render(, 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};
    },
    componentWillMount: function () {
        console.log("A componentWillMount");
    },
    componentDidMount: function () {
        this.setState({init: true});
        console.log("A componentDidMount:" + this.state.init);
    },
    render: function () {
        console.log("A render:" + this.state.init);
        return null;
    }
});
React.render(, document.getElementById("example"));

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

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

var A = React.createClass({
    getInitialState: function () {
        return {init: false};
    },
    componentWillMount: function () {
        console.log("A componentWillMount");
    },
    componentDidMount: function () {
        this.setState({init: true}, function () {
            console.log("callback:" + this.state.init);
        });
        console.log("A componentDidMount");
    },
    render: function () {
        console.log("A render:" + this.state.init);
        return null;
    }
});
React.render(, 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 ?
                
    {this.state.data.map(function (item) { return
  • {item}
  • })}
:
loading data...
) } }); React.render(
, 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 ;
    }
});
React.render(, 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(, document.getElementById("example"));

控制台啥也没打印

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

var Child = React.createClass({
    componentWillReceiveProps: function (nextProps) {
        console.log("Child componentWillReceiveProps");
    },
    render: function () {
        return 
{this.props.name}
; } }); var Parent = React.createClass({ getInitialState: function () { return {name: "xxx"}; }, componentDidMount: function () { this.setState({name: "zzz"}); }, render: function () { return ; } }); React.render(, 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 . 
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 ? 
{this.props.name}
: null; } }); var Parent = React.createClass({ getInitialState: function () { return {name: "xxx"}; }, componentDidMount: function () { this.setState({name: "xxx"}); }, render: function () { return ; } }); React.render(, 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(, 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");
    },
    shouldComponentUpdate: function (nextProps, 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 ;
    }
});

React.render(, 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 ;
    }
});

React.render(, document.getElementById("example"));

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/85954.html

相关文章

  • React V16.3生命周期:The Component Lifecycle Flowchart

    摘要:学习免不了对组件生命周期的学习,我们应该掌握最新生命周期,学以致用,以达到性能优化的目的。 学习React免不了对组件生命周期的学习,我们应该掌握最新生命周期,学以致用,以达到性能优化的目的。 The Component Lifecycle React Version: 16.3 1 生命周期可视化 高清大图--欢迎转载 showImg(https://segmentfault.co...

    curried 评论0 收藏0
  • 【译】State and Lifecycle (State和生命周期)

    摘要:结果如下打开试试下一步,我们将把组件功能自己设置定时器并且能每秒更新。这是一个设置定时器的好地方注意我们是怎么保存定时器的。我们将在这个生命周期的函数方法中卸载掉定时器最后,我们会每一秒跑方法。 下面是react官方文档的个人翻译,如有翻译错误,请多多指出原文地址:https://facebook.github.io/re... Consider the ticking clock e...

    dadong 评论0 收藏0
  • react.js入门篇

    摘要:搭建项目,引入既然学习,那就从开始。项目工程目录如下编写在我理解看来,项目文件入口即是文件,那么从中编写。官方文档所述图片注文章末尾段组件生命周期引入自 Hello Word 1.搭建项目,引入react 既然学习react,那就从hello word开始。当然必不可少的需要引入react,这里我使用的是官网的 Creating a New Application 方式,通过以下命令...

    harryhappy 评论0 收藏0
  • React中的“虫洞”——Context

    摘要:理论上,通过一层层传递下去当然是没问题的。不过这也太麻烦啦,要是能在最外层和最里层之间开一个穿越空间的虫洞就好了。使用看起来很高大上的使用起来却异常简单。就像中的全局变量,只有真正全局的东西才适合放在中。 当我们写React时,我们总是通过改变State和传递Prop对view进行控制,有时,也会遇到一点小麻烦。 背景 但是随着我们的应用变的越来越复杂,组件嵌套也变的越来越深,有时甚至...

    muddyway 评论0 收藏0
  • 用 Vue 来写 ReactReactNative

    摘要:的响应核心的响应式系统是支撑整个框架运行的关键,也是的核心之一,官方对这个核心的分层设计得很好也是依靠其驱动原生视图。我们日常用到的都由核心提供,对这个核心稍作修改,去掉和,意外的获得了一个极小的响应核心,可以运行于任何标准引擎下。 showImg(https://segmentfault.com/img/bVPMZy?w=1468&h=826); GitHub: react-vue ...

    maybe_009 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<