React:组件的生命周期

在组件的整个生命周期中,随着该组件的props或者state发生改变,其DOM表现也会有相应的变化。一个组件就是一个状态机,对于特定地输入,它总返回一致的输出。html

一个React组件的生命周期分为三个部分:实例化、存在期和销毁时。react

实例化

当组件在客户端被实例化,第一次被建立时,如下方法依次被调用:git

一、getDefaultProps
二、getInitialState
三、componentWillMount
四、render
五、componentDidMountcanvas

当组件在服务端被实例化,首次被建立时,如下方法依次被调用:数据结构

一、getDefaultProps
二、getInitialState
三、componentWillMount
四、renderui

componentDidMount 不会在服务端被渲染的过程当中调用。this

getDefaultProps

对于每一个组件实例来说,这个方法只会调用一次,该组件类的全部后续应用,getDefaultPops 将不会再被调用,其返回的对象能够用于设置默认的 props(properties的缩写) 值。spa

var Hello = React.creatClass({
    getDefaultProps: function(){
        return {
            name: 'pomy',
            git: 'dwqs'
        }
    },
    
    render: function(){
        return (
            <div>Hello,{this.props.name},git username is {this.props.dwqs}</div>
        )
    }
});

ReactDOM.render(<Hello />, document.body);

也能够在挂载组件的时候设置 props:日志

var data = [{title: 'Hello'}];
<Hello data={data} />

或者调用 setProps (通常不须要调用)来设置其 props:code

var data = [{title: 'Hello'}];
var Hello = React.render(<Demo />, document.body);
Hello.setProps({data:data});

但只能在子组件或组件树上调用 setProps。别调用 this.setProps 或者 直接修改 this.props。将其当作只读数据。

React经过 propTypes 提供了一种验证 props 的方式,propTypes 是一个配置对象,用于定义属性类型:

var survey = React.createClass({
    propTypes: {
        survey: React.PropTypes.shape({
            id: React.PropTypes.number.isRequired
        }).isRequired,
        onClick: React.PropTypes.func,
        name: React.PropTypes.string,
        score: React.PropTypes.array
        ...
    },
    
    //...
})

组件初始化时,若是传递的属性和 propTypes 不匹配,则会打印一个 console.warn 日志。若是是可选配置,能够去掉.isRequired。经常使用的 PropTypes 以下:

PropTypes

getInitialState

对于组件的每一个实例来讲,这个方法的调用有且只有一次,用来初始化每一个实例的 state,在这个方法里,能够访问组件的 props。每个React组件都有本身的 state,其与 props 的区别在于 state只存在组件的内部,props 在全部实例中共享。

getInitialState 和 getDefaultPops 的调用是有区别的,getDefaultPops 是对于组件类来讲只调用一次,后续该类的应用都不会被调用,而 getInitialState 是对于每一个组件实例来说都会调用,而且只调一次。

var LikeButton = React.createClass({
  getInitialState: function() {
    return {liked: false};
  },
  handleClick: function(event) {
    this.setState({liked: !this.state.liked});
  },
  render: function() {
    var text = this.state.liked ? 'like' : 'haven\'t liked';
    return (
      <p onClick={this.handleClick}>
        You {text} this. Click to toggle.
      </p>
    );
  }
});

ReactDOM.render(
  <LikeButton />,
  document.getElementById('example')
);

每次修改 state,都会从新渲染组件,实例化后经过 state 更新组件,会依次调用下列方法:

一、shouldComponentUpdate
二、componentWillUpdate
三、render
四、componentDidUpdate

可是不要直接修改 this.state,要经过 this.setState 方法来修改。

componentWillMount

该方法在首次渲染以前调用,也是再 render 方法调用以前修改 state 的最后一次机会。

render

该方法会建立一个虚拟DOM,用来表示组件的输出。对于一个组件来说,render方法是惟一一个必需的方法。render方法须要知足下面几点:

  1. 只能经过 this.props 和 this.state 访问数据(不能修改)

  2. 能够返回 null,false 或者任何React组件

  3. 只能出现一个顶级组件,不能返回一组元素

  4. 不能改变组件的状态

  5. 不能修改DOM的输出

render方法返回的结果并非真正的DOM元素,而是一个虚拟的表现,相似于一个DOM tree的结构的对象。react之因此效率高,就是这个缘由。

componentDidMount

该方法不会在服务端被渲染的过程当中调用。该方法被调用时,已经渲染出真实的 DOM,能够再该方法中经过 this.getDOMNode() 访问到真实的 DOM(推荐使用 ReactDOM.findDOMNode())。

var data = [..];
var comp = React.createClass({
    render: function(){
        return <imput .. />
    },
    componentDidMount: function(){
        $(this.getDOMNode()).autoComplete({
            src: data
        })
    }
})

因为组件并非真实的 DOM 节点,而是存在于内存之中的一种数据结构,叫作虚拟 DOM (virtual DOM)。只有当它插入文档之后,才会变成真实的 DOM 。有时须要从组件获取真实 DOM 的节点,这时就要用到 ref 属性:

var Area = React.createClass({
    render: function(){
        this.getDOMNode(); //render调用时,组件未挂载,这里将报错
        
        return <canvas ref='mainCanvas'>
    },
    componentDidMount: function(){
        var canvas = this.refs.mainCanvas.getDOMNode();
        //这是有效的,能够访问到 Canvas 节点
    }
})

须要注意的是,因为 this.refs.[refName] 属性获取的是真实 DOM ,因此必须等到虚拟 DOM 插入文档之后,才能使用这个属性,不然会报错。

存在期

此时组件已经渲染好而且用户能够与它进行交互,好比鼠标点击,手指点按,或者其它的一些事件,致使应用状态的改变,你将会看到下面的方法依次被调用

一、componentWillReceiveProps
二、shouldComponentUpdate
三、componentWillUpdate
四、render
五、componentDidUpdate

componentWillReceiveProps

组件的 props 属性能够经过父组件来更改,这时,componentWillReceiveProps 未来被调用。能够在这个方法里更新 state,以触发 render 方法从新渲染组件。

componentWillReceiveProps: function(nextProps){
    if(nextProps.checked !== undefined){
        this.setState({
            checked: nextProps.checked
        })
    }
}

shouldComponentUpdate

若是你肯定组件的 props 或者 state 的改变不须要从新渲染,能够经过在这个方法里经过返回 false 来阻止组件的从新渲染,返回 `false 则不会执行 render 以及后面的 componentWillUpdate,componentDidUpdate 方法。

该方法是非必须的,而且大多数状况下没有在开发中使用。

shouldComponentUpdate: function(nextProps, nextState){
    return this.state.checked === nextState.checked;
    //return false 则不更新组件
}

componentWillUpdate

这个方法和 componentWillMount 相似,在组件接收到了新的 props 或者 state 即将进行从新渲染前,componentWillUpdate(object nextProps, object nextState) 会被调用,注意不要在此方面里再去更新 props 或者 state。

componentDidUpdate

这个方法和 componentDidMount 相似,在组件从新被渲染以后,componentDidUpdate(object prevProps, object prevState) 会被调用。能够在这里访问并修改 DOM。

销毁时

componentWillUnmount

每当React使用完一个组件,这个组件必须从 DOM 中卸载后被销毁,此时 componentWillUnmout 会被执行,完成全部的清理和销毁工做,在 componentDidMount 中添加的任务都须要再该方法中撤销,如建立的定时器或事件监听器。

当再次装载组件时,如下方法会被依次调用:

一、getInitialState
二、componentWillMount
三、render
四、componentDidMount

反模式

在 getInitialState 方法中,尝试经过 this.props 来建立 state 的作法是一种反模式。

//反模式
getDefaultProps: function(){
    return {
        data: new Date()
    }
},
getInitialState: function(){
    return {
        day: this.props.date - new Date()
    }
},
render: function(){
    return <div>Day:{this.state.day}</div>
}

通过计算后的值不该该赋给 state,正确的模式应该是在渲染时计算这些值。这样保证了计算后的值永远不会与派生出它的 props 值不一样步。

//正确模式
getDefaultProps: function(){
    return {
        data: new Date()
    }
},
render: function(){
    var day = this.props.date - new Date();
    return <div>Day:{day}</div>
}

若是只是简单的初始化 state,那么应用反模式是没有问题的。

总结

如下面的一张图总结组件的生命周期:

clipboard.png

原文:http://www.ido321.com/1653.html