State & 生命週期
狀態與屬性十分相似,但是狀態是私有的,完全受控於當前組件。
我們之前提到過,定義爲類的組件有一些特性。局部狀態就是如此:一個功能只適用於類。
將函數轉換爲類
你可以通過5個步驟將函數組件 Clock
轉換爲類
創建一個名稱擴展爲
React.Component
的ES6 類創建一個叫做
render()
的空方法將函數體移動到
render()
方法中在
render()
方法中,使用this.props
替換props
刪除剩餘的空函數聲明
爲一個類添加局部狀態
我們會通過3個步驟將 date
從屬性移動到狀態中:
- 在
render()
方法中使用this.state.date
替代this.props.date
- 添加一個類構造函數來初始化狀態
this.state
class Clock extends React.Component {
constructor(props) {
super(props);
this.state = {date: new Date()};
}
render() {
return (
<div>
<h1>Hello, world!</h1>
<h2>It is {this.state.date.toLocaleTimeString()}.</h2>
</div>
);
}
}
類組件應始終使用props
調用基礎構造函數。
將生命週期方法添加到類中
在具有許多組件的應用程序中,在銷燬時釋放組件所佔用的資源非常重要。
每當Clock
組件第一次加載到DOM中的時候,我們都想生成定時器,這在React中被稱爲掛載
同樣,每當Clock
生成的這個DOM被移除的時候,我們也會想要清除定時器,這在React中被稱爲卸載
。
我們可以在組件類上聲明特殊的方法,當組件掛載或卸載時,來運行一些代碼:
class Clock extends React.Component {
constructor(props) {
super(props);
this.state = {date: new Date()};
}
componentDidMount() {
}
componentWillUnmount() {
}
render() {
return (
<div>
<h1>Hello, world!</h1>
<h2>It is {this.state.date.toLocaleTimeString()}.</h2>
</div>
);
}
}
這些方法被稱作生命週期鉤子
。
當組件輸出到 DOM 後會執行 componentDidMount()
鉤子,這是一個建立定時器的好地方:
componentDidMount() {
this.timerID = setInterval(
() => this.tick(),
1000
);
}
注意我們如何在 this
中保存定時器ID。
雖然 this.props
由React本身設置以及this.state
具有特殊的含義,但如果需要存儲不用於視覺輸出的東西,則可以手動向類中添加其他字段。
如果你不在 render()
中使用某些東西,它就不應該在狀態中。
我們將在 componentWillUnmount()
生命週期鉤子中卸載計時器:
componentWillUnmount() {
clearInterval(this.timerID);
}
最後,我們實現了每秒鐘執行的 tick()
方法。
它將使用 this.setState()
來更新組件局部狀態:
class Clock extends React.Component {
constructor(props) {
super(props);
this.state = {date: new Date()};
}
componentDidMount() {
this.timerID = setInterval(
() => this.tick(),
1000
);
}
componentWillUnmount() {
clearInterval(this.timerID);
}
tick() {
this.setState({
date: new Date()
});
}
render() {
return (
<div>
<h1>Hello, world!</h1>
<h2>It is {this.state.date.toLocaleTimeString()}.</h2>
</div>
);
}
}
ReactDOM.render(
<Clock />,
document.getElementById('root')
);
現在時鐘每秒鐘都會執行。
讓我們快速回顧一下發生了什麼以及調用方法的順序:
當
<Clock />
被傳遞給ReactDOM.render()
時,React 調用Clock
組件的構造函數。 由於Clock
需要顯示當前時間,所以使用包含當前時間的對象來初始化this.state
。 我們稍後會更新此狀態。React 然後調用
Clock
組件的render()
方法。這是 React 瞭解屏幕上應該顯示什麼內容,然後 React 更新 DOM 以匹配Clock
的渲染輸出。當
Clock
的輸出插入到 DOM 中時,React 調用componentDidMount()
生命週期鉤子。 在其中,Clock
組件要求瀏覽器設置一個定時器,每秒鐘調用一次tick()
。瀏覽器每秒鐘調用
tick()
方法。 在其中,Clock
組件通過使用包含當前時間的對象調用setState()
來調度UI更新。 通過調用setState()
,React 知道狀態已經改變,並再次調用render()
方法來確定屏幕上應當顯示什麼。 這一次,render()
方法中的this.state.date
將不同,所以渲染輸出將包含更新的時間,並相應地更新DOM。一旦
Clock
組件被從DOM中移除,React會調用componentWillUnmount()
這個鉤子函數,定時器也就會被清除。
class Clock extends React.Component {
constructor(props) {
super(props);
this.state = {date: new Date()};
}
componentDidMount() {
this.timerID = setInterval(
() => this.tick(),
1000
);
}
componentWillUnmount() {
clearInterval(this.timerID);
}
tick() {
this.setState({
date: new Date()
});
}
render() {
return (
<div>
<h1>Hello, world!</h1>
<h2>It is {this.state.date.toLocaleTimeString()}.</h2>
</div>
);
}
}
ReactDOM.render(
<Clock />,
document.getElementById('root')
);