在上個章節中了解了 state 的概念,就讓我們來重新重溫一下。
State 代表 React component 的狀態,會是一個完全由創建的 component 維護 / 控制的 JavaScript object,常用於存放 component 自己需要的資料。
當 component 需要擁有私有資料 / 狀態,而上層的 component 不需要這些狀態時就是使用 state 的好時機。
State 的常用的情境包括時鐘的時間、menu 開合與否、server API 呼叫 ...etc。
另外,使用 state 可獲得的好處是可以封裝邏輯 component 的實作細節。
最後,操作 state 通常要經過兩個階段:
到目前為止,這個段落回顧了 state 的基本介紹與使用方式,接著是時候實際了解 state 的語法了!
在接下來的段落中,我們將針對 class component state 的語法介紹。Function component 支援 state 的方式(Hooks)則會在之後的篇章詳細說明。
在 class component 中,this.state
即代表 component 的 state。
如本篇開頭介紹所說,this.state
因為可以為任意的 JavaScript object,所以初始化 state 時只要將目標資料賦值(assign)給 this.state
即可。
另外,類似其他物件導向語法,JavaScript 在創建 class 的 Instance 時會先執行 constructor
以初始化物件,因此初始化 this.state
的動作也應該在 constructor
裡做,語法會如下:
class YourComponent extends React.Component {
constructor(props) {
super(props);
this.state = { /* some data */ };
}
render() {
return /* some data */;
}
}
接著就讓我們動手開始做一個時鐘 component:
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>
);
}
}
ReactDOM.render(
<Clock />,
document.getElementById('root')
);
因為時間是時鐘 component 自己需要知道的資料,不用被上層所知道,而時鐘 component 也有辦法自己取得當前時間,因此是個用 state 的好時機。
首先創建了一個名為 Clock
的 class component。在 constructor
中則先初始化 state 的內容以紀錄當前的時間,接著 render
中則顯示文字與 state 裡紀錄的時間資訊。
值得注意的是因為繼承了 React.Component
,且 React.Component
需要知道 props 為何,因此我們必需要使用 super
將 props 傳入父類別(Base class)中。詳細請參考 ES6 Class 語法和 React.Component API。
更新 state 需要用到 React 的 setState
API,以下將介紹其用途與使用方式。
如上一篇中的 簡介 state 使用概念 所介紹,setState
有兩個功能:
render()
以重新渲染畫面在更新完 state 後,React 會重新執行 render()
函式,將新的狀態資料更新到畫面上。
setState
API 有兩個參數且沒有回傳值。第一個參數是用來取得新的 state,第二個參數則是更新完 state 後要接續執行的 callback function,如下所示:
setState(stateChange[, callback])
// or
setState(updater[, callback])
需要注意的是,第一個參數可支援兩種格式:
stateChange
:代表新的 state,為 JavaScript object。updater
:代表產生新的 state object 的 function。支援從當前的 state 與 props 產出新的 state。第二個參數則是剛剛提到的:
callback
:代表更新完 state 後要觸發的 function。此 function 不帶任何參數且沒有回傳值。以下我們將針對第一個參數的兩種 API 格式:stateChange
與 updater
介紹用法。
我們先來深入了解 stateChange
這個參數的使用情境與語法:
setState(stateChange[, callback])
stateChange
代表新的 state,會是一個 JavaScript object。
stateChange
的使用時機是在不需要由當前的 state 與 props 就可以產出新的 state 時使用。
較特別的地方是,新的 state object 只要包含要更新的 state property 即可,不用將舊有的 state 資料全部帶入。React 會自動幫我們把新的 State 與舊的 State Shallow Merge。
回到時鐘的範例,我們現在要讓時鐘每秒都更新一次時間:
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')
);
為了讓時間每秒更新,我們在 class component 的 lifecycle 函式:componentDidMount
中設定 timer 每秒觸發時間更新 tick
。
在 tick
中,則利用 setState
更新狀態,最後 React 會幫我們觸發 render()
更新畫面上的時間。
值得注意的一點是,就算沒有當前的 state 與 props 也可以拿到現在時間(new Date()
),因此使用 stateChange
參數帶入全新的資料即可,不需要使用到 updater
參數。
最後,當 component 之後要被刪除時,React 就會觸發 Clock
component 的 componentWillUnmount
去把一開始設定的 setTimeout
清除。
componentDidMount
與 componentWillUnmount
都是 class component 內建的lifecycle 函式。React 會在對應 component 的生命週期去呼叫其 lifecycle 函式。更多的內容將在往後的章節中介紹。恭喜,在這個範例中已經成功完成時鐘 Component 了!讀者有興趣的話也可以在 CodePen 上試試。
接著我們來了解 updater
這個參數的使用情境與語法:
setState(updater[, callback])
updater
代表產生新的 state object 的 function,此 function 接受兩個參數:
state
:當前的 stateprops
:當前的 propsupdater
的使用時機是在需要由當前的 state 與 props 產出新的 State 時使用。
同樣的, updater
產出的新的 state object 包含要更新的 state property 即可,不用將舊有的 state property 全部帶入。React 會自動幫我們把新的 state 與舊的 state Shallow Merge。
雖然在介紹 stateChange
範例時就提過,並不需要 updater
也可以做完 Clock
component,但我們依然可以嘗試使用 updater
來做到每秒更新時間的功能。
再嘗試用新方法做一次時鐘吧:
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((state, props) => ({
date: new Date(state.date.getTime() + 1000)
}));
}
render() {
return (
<div>
<h1>Hello, world!</h1>
<h2>It is {this.state.date.toLocaleTimeString()}.</h2>
</div>
);
}
}
ReactDOM.render(
<Clock />,
document.getElementById('root')
);
主要的實作概念就是每秒把當前的 this.state.date
加上 1000 毫秒。
為了要做到當前的 state 加上 1000 毫秒這件事,需要把 tick
中的 setState
改成使用 updater
函式。updater
的第一個參數會是當前的狀態 state
,把這個參數帶入後,再將 Date 的語法改為 目前時間 + 1000
即大功告成了!
太棒了!在這個範例中我們用另一個方式完成了時鐘 Component,也更深入的了理解了 updater
的用法。想要看看完整範例的讀者可以從 CodePen 查看結果。
這次的章節中介紹了 class component state 的語法:
constructor
中初始化 this.state
this.setState
設定新的 state下一章節中將更深入的介紹 State 的一些特性以及內部運作原理。
想問下 選擇寫class component的人會比較少嗎 因為比較喜歡class的寫法!!!
目前社群主流感覺是比較偏向 function components + hooks 的寫法。
但個人認為如果自己跟團隊都能接受 class component 的話,使用 class component 語法也沒有什麼不好~