咱们先来理一理React的生命周期方法有哪些:ajax
渲染前调用一次,这个时候DOM结构尚未渲染。服务器
渲染完成后调用一次,这个时候DOM结构已经渲染了。这个时候就能够初始化其余框架的设置了,若是利用jQuery绑定事件等等。框架
初始化渲染不会调用,在接收到新的props时,会调用这个方法。异步
初始化渲染不会调用,接收到新的props或state时调用。函数
初始化渲染不会调用,更新前调用。性能
初始化渲染不会调用,更新后调用。fetch
组件移除前调用。
根据执行的时机,这些方法能够分为三类。this
组件渲染先后会执行,并且只会执行一次,看个例子code
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里容许咱们初始化前最后一次对state进行修改,而不会触发从新渲染。component
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(<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'); }, render: function () { console.log('A render:' + this.state.init); return null; } }); React.render(<A />, 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(<A />, 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(<A />, document.getElementById('example')); #控制台打印 A componentWillMount A render:false A componentDidMount A render:true callback:true
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
更新方法只会在组件初始化渲染完成后且触发了从新渲染的条件才会执行。更新方法同挂载方法分处组件生命周期的不一样的阶段。例如一个婴儿在出生前和出生后,这是两个不一样的阶段。
组件接收到新的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'}; }, componentDidMount: function () { this.setState({name: 'zzz'}); }, 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'}); }, render: function () { return <Child name={this.state.name}/>; } }); React.render(<Parent />, document.getElementById('example'));
咱们要尽可能避免父子组件当中都有state,这样组件的复用性就会下降,通常来讲保持最外层的容器组件同服务器、用户交互,改变state,而子组件只负责经过props接收数据,而后渲染页面。这也是官方推荐的作法。
更新前调用,返回值决定了组件是否更新。例如
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方法没有影响。
组件更新先后执行,没办法决定组件是否更新,只能进行些非状态的操做,我的感受用途不太明显。
组件更新的整个过程
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 <Child />; } }); React.render(<Parent />, document.getElementById('example')); #控制台打印 Child render Child componentWillReceiveProps Child shouldComponentUpdate Child componentWillUpdate Child render Child componentDidUpdate
第一个render是初始化调用的,不是更新的过程。
组件被移除前调用,这里能够作一些清除工做,例如清除内存,解除事件的监听等等。
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'));