咱們先來理一理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'));