什麼是生命週期函數?
就是不用本身手動的去調用,框架會在合適的時間自動的調用該函數react
生命週期函數分類:git
生命週期函數執行流程:
算法
constructor
是ES6對類的默認方法,經過 new
命令生成對象實例時自動調用該方法(調用一次)。
而且,該方法是類中必須有的,若是沒有顯示定義,則會默認添加空的constructor()
方法。當存在constructor
的時候必須手動調用super
方法。
在constructor
中若是要訪問this.props
,則須要傳入props
,bash
class Counter extends React.component{
constructor(props){
super(props); // 聲明constructor時必須調用super方法
console.log(this.props); // 能夠正常訪問this.props
}
}
複製代碼
constructor 經常使用來初始化state框架
constructor(props){
super(props)
this.state = {number:0} // 初始化的狀態
}
複製代碼
在類組件中也能夠經過靜態方法設置屬性dom
class Counter extends React.component{
static defaultProps = {name:"Fan"} // 默認屬性
constructor(props){
super(props); // 聲明constructor時必須調用super方法
console.log(this.props); // 能夠正常訪問this.props
}
}
複製代碼
在組件掛載以前調用,且全局只調用一次。異步
若是在這個鉤子裏調用this.setState
不會引發組件從新渲染,也能夠把寫在這裏的內容寫道到constructor()
中,因此不多使用。ide
render是一個React組件必須定義的生命週期函數,用來渲染DOM。函數
並必須 return
一個React元素(描述組件,即UI),不負責組件實際渲染工做,以後由React自身根據此元素去渲染出頁面DOM。
render必須是純函數(Pure function:函數的返回結果只依賴於它的參數;函數執行過程裏面沒有反作用)性能
不要在render裏面修改state,會觸發死循環致使棧溢出。
在組件掛載完成後調用,且全局只調用一次。
能夠在這裏使用refs,獲取真實dom元素。
該鉤子內也能夠發起異步請求,並在異步請求中能夠進行setState
。
在講述此階段前須要先明確下react組件更新機制。
setState
引發的state
更新或父組件從新render
引發的props
更新,更新後的state
和props
相對以前不管是否有變化,都將引發子組件的從新render
。
props
發生變化以及父組件從新渲染時都會觸發該生命週期函數
在該鉤子內能夠經過參數nextProps
獲取變化後的props
參數,
經過this.props
訪問以前的props
。該生命週期內能夠進行setState
。
組件掛載以後,每次調用setState
後都會調用shouldComponentUpdate
判斷是否須要從新渲染組件。就是經過比較nextProps
,nextState
及當前組件的this.props
,this.state
的狀態用來判斷是否須要從新渲染
默認返回true
,須要從新render
,返回false
則不觸發渲染。
通常咱們經過該函數來優化性能
例如:一個React項目須要更新一個小組件時,極可能須要父組件更新本身的狀態。而一個父組件的從新更新會形成它其下全部的子組件從新執行render()
方法,造成新的虛擬DOM,再用diff算法對新舊虛擬DOM進行結構和屬性的比較,決定組件是否須要從新渲染
無疑這樣的操做會形成不少的性能浪費,因此咱們開發者能夠根據項目的業務邏輯,在shouldComponentUpdate()
中加入條件判斷,從而優化性能
組件即將被更新時觸發
shouldComponentUpdate
返回true
或者調用forceUpdate
以後,componentWillUpdate
會被調用。
不能在該鉤子中setState
,不然會觸發重複循環。
此方法在組件更新後被調用(除了首次render
以後調用componentDidMount
,其它render
結束以後都是調用componentDidUpdate
)
能夠操做組件更新的DOM,prevProps
和prevState
這兩個參數指的是組件更新前的props
和state
該鉤子內
setState
有可能會觸發重複渲染,須要自行判斷,不然會進入死循環。
此方法在組件被卸載前調用
能夠在這裏執行一些清理工做,好比清除組件中使用的定時器、取消Redux的訂閱事件、清除componentDidMount
中手動建立的DOM元素等等,以免引發內存泄漏。
import React, { Component } from 'react';
class Father extends React.Component{
static defaultProps = {name:"Fan"} // 默認屬性
constructor(props){
super(props)
this.state = {number:0}
}
UNSAFE_componentWillMount(){
console.log("Father: componentWillMount") // 1
}
shouldComponentUpdate(nextProps,nextState){
console.log("Father: shouldComponentUpdate") //
// if(nextState.number%2 === 0){
// return true
// }else{
// return false;
// }
return true;
}
componentWillUpdate(){
console.log("Father: componentWillUpdate") //
}
componentDidUpdate(){
console.log("Father: componentDidUpdate")
}
handleClick = ()=>{
this.setState({number:this.state.number+1})
}
render(){
console.log("Father: render") // 2
return(
<div>
<h1>父組件</h1>
<p>{this.state.number}</p>
<button onClick={this.handleClick}>+</button>
<hr/>
{this.state.number%2==0 ? <Son number={this.state.number}></Son> : null}
</div>
)
}
componentDidMount(){
console.log("Father: componentDidMount") // 3
}
}
class Son extends React.Component{
UNSAFE_componentWillMount(){
console.log("Son: componentWillMount") // 1
}
componentDidMount(){
console.log("Son: componentDidMount") // 3
}
componentWillReceiveProps(){
console.log("Son: componentWillReceiveProps")
}
shouldComponentUpdate(){
console.log("Son: shouldComponentUpdate")
// return false;
return true;
}
componentWillUpdate(){
console.log("Son: componentWillUpdate") //
}
componentDidUpdate(){
console.log("Son: componentDidUpdate")
}
render(){
console.log("Son: render")
return(
<div>
<h1>子組件</h1>
<p>{this.props.number}</p>
</div>
)
}
componentWillUnmount(){
console.log("Son: componentWillUnmount")
}
}
export default Father;
複製代碼
運行結果(須要下去本身慢慢分析):
移除的生命週期函數:
雖然廢棄了這三個生命週期方法,可是爲了向下兼容,將會作漸進式調整。
在16.3 版本並未刪除這三個生命週期,同時還爲它們新增以UNSAFE_
前綴爲別名的三個函數UNSAFE_componentWillMount()
、UNSAFE_componentWillReceiveProps()
、UNSAFE_componentWillUpdate()
。
在 16.4 版本給出警告將會棄用componentWillMount()
、componentWillReceiveProps()
、componentWillUpdate()
三個函數
而後在 17.0 版本將會刪除componentWillMount()
、componentWillReceiveProps()
、componentWillUpdate()
這三個函數,會保留使用UNSAFE_componentWillMount()
、UNSAFE_componentWillReceiveProps()
、UNSAFE_componentWillUpdate()
新增的聲明周期函數:
從上面的生命週期的圖中能夠看出,被廢棄的三個函數都是在render
以前,由於fiber的出現,極可能由於高優先級任務的出現而打斷現有任務致使它們會被執行屢次,這確定不是你想要的結果。
另外的一個緣由則是,React想約束使用者,好的框架可以讓人不得已寫出容易維護和擴展的代碼。
有篇文章感受寫的很好新老生命週期對比
在每次渲染以前都會調用,無論形成從新渲染的緣由是什麼,無論初始掛載仍是後面的更新都會調用,這一點和UNSAFE_componentWillReceiveProps
不一樣(只有當父組件形成從新渲染時才調用)
該方法傳入的兩個參數:nextProps
表示父組件傳入的值,prevState
表示組件自身的state
使用該方法,須要在該方法中返回一個對象或null:若是返回的是對象,則會更新 state,若是返回的是null,則表示不更新。
使用該方法的時候須要初始化state,不然在控制檯中會出現警告信息,不能在該方法內部,調用
this.state
這個方法在 render()
以後,componentDidUpdate()
以前調用。
該方法傳入的兩個參數:prevProps
表示更新前的 props,prevState
表示更新前的 state
返回值稱爲一個快照(snapshot),若是不須要 snapshot,則必須顯示的返回 null
—— 由於返回值將做爲 componentDidUpdate()
的第三個參數使用。因此這個函數必需要配合 componentDidUpdate()
一塊兒使用。
這個函數的做用是在真實 DOM 更新(
componentDidUpdate
)前,獲取一些須要的信息(相似快照功能),而後做爲參數傳給componentDidUpdate
。例如:在getSnapShotBeforeUpdate
中獲取滾動位置,而後做爲參數傳給componentDidUpdate
,就能夠直接在渲染真實的 DOM 時就滾動到須要的位置。
在上面沒說,這裏說一下
任何子組件在渲染期間,生命週期方法中或者構造函數 constructor
發生錯誤時調用。
該方法傳入的兩個參數:err
表示拋出的錯誤,info
表示帶有componentStack key
的對象,其中包含有關組件引起錯誤的棧信息。
錯誤邊界不會捕獲下面的錯誤:
import React, { Component } from 'react';
class Father extends Component{
constructor(props){
super(props)
this.state = {number:0}
}
handleClick = ()=>{
this.setState({number:this.state.number+1})
}
render(){
return(
<div>
<h1>父組件</h1>
<p>{this.state.number}</p>
<button onClick={this.handleClick}>+</button>
<hr/>
<Son number={this.state.number}></Son>
</div>
)
}
}
class Son extends React.Component{
state = {
number:0
}
// nextProps,prevState
// nextProps 就是父給子傳遞的過來的新的數據
// prevState 是子的上一次的狀態
//每次更新都會觸發
static getDerivedStateFromProps(nextProps,prevState){
// nextProps 表示父向下傳遞的新的值 1 2 3 4 5
// prevState 0 0 0
console.log(nextProps,prevState) // {number: 0} {number: 0}
let {number} = nextProps;
// console.log(number)
// if(number%2 === 0){
// return { number:number+10 }
// }else{
// return { number:number+100 }
// }
// prevState 表示是子組件的上一次狀態
if(number%2 === 0){
console.log(prevState.number)
return { number:prevState.number + number+10 }
}else{
console.log(prevState.number)
return { number:prevState.number + number+100 }
}
}
handleChange = ()=>{
this.setState({
number:this.state.number+1000
})
}
render(){
return(
<div>
<h1>子組件</h1>
<p>{this.state.number}</p>
<button onClick={this.handleChange}>改變狀態</button>
</div>
)
}
}
export default Father;
複製代碼
運行效果:
import React, { Component } from 'react';
class News extends Component {
constructor(props) {
super(props)
this.scrollRef = React.createRef();
}
state = {
// news:["news6","news5","new4","news3","news2","new1"]
news: []
}
componentDidMount() {
this.timer = setInterval(() => {
this.setState({
news: [`${this.state.news.length}`, ...this.state.news]
})
}, 1000)
}
componentWillUnmount() {
clearInterval(this.timer)
}
// 獲取更新以前dom的快照
getSnapshotBeforeUpdate() {
// console.log(this.scrollRef.current.scrollHeight)
// 在getSnapshotBeforeUpdate中返回了一個值,這個值會給componedDidUpdate的最後一個參數
return this.scrollRef.current.scrollHeight;
}
componentDidUpdate(prevProps, prevState, lastScrollHeight) {
console.log(lastScrollHeight)
console.log(this.scrollRef.current.scrollTop) // 0
let scrollTop = this.scrollRef.current.scrollTop;
this.scrollRef.current.scrollTop = scrollTop + (this.scrollRef.current.scrollHeight - lastScrollHeight)
}
render() {
let styles = {
height: "100px",
width: "200px",
border: "1px solid red",
overflow: "auto"
}
return (
<ul style={styles} ref={this.scrollRef}>
{
this.state.news.map((item, index) => <li key={index}>{item}</li>)
}
</ul>
)
}
}
export default News;
複製代碼
運行效果: