iT邦幫忙

第 12 屆 iT 邦幫忙鐵人賽

DAY 9
1
Modern Web

I Want To Know React系列 第 9

I Want To Know React - Class Component State 語法

回顧 state

上個章節中了解了 state 的概念,就讓我們來重新重溫一下。

State 代表 React component 的狀態,會是一個完全由創建的 component 維護 / 控制的 JavaScript object,常用於存放 component 自己需要的資料。

當 component 需要擁有私有資料 / 狀態,而上層的 component 不需要這些狀態時就是使用 state 的好時機。

State 的常用的情境包括時鐘的時間、menu 開合與否、server API 呼叫 ...etc。

另外,使用 state 可獲得的好處是可以封裝邏輯 component 的實作細節。

最後,操作 state 通常要經過兩個階段:

  • 初始 state
  • 更新 state

到目前為止,這個段落回顧了 state 的基本介紹與使用方式,接著是時候實際了解 state 的語法了!

初始 State 語法

在接下來的段落中,我們將針對 class component state 的語法介紹。Function component 支援 state 的方式(Hooks)則會在之後的篇章詳細說明。

初始化 State 語法

在 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

更新 React Component State 語法

更新 state 需要用到 React 的 setState API,以下將介紹其用途與使用方式。

setState 用途

上一篇中的 簡介 state 使用概念 所介紹,setState 有兩個功能:

  • 更新 state
  • 觸發 render() 以重新渲染畫面

在更新完 state 後,React 會重新執行 render() 函式,將新的狀態資料更新到畫面上。

setState 語法

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 格式:stateChangeupdater 介紹用法。

stateChange 參數

我們先來深入了解 stateChange 這個參數的使用情境與語法:

setState(stateChange[, callback])

stateChange 代表新的 state,會是一個 JavaScript object。

stateChange 的使用時機是在不需要由當前的 state 與 props 就可以產出新的 state 時使用。

較特別的地方是,新的 state object 只要包含要更新的 state property 即可,不用將舊有的 state 資料全部帶入。React 會自動幫我們把新的 State 與舊的 State Shallow Merge

stateChange 參數範例

回到時鐘的範例,我們現在要讓時鐘每秒都更新一次時間:

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 清除。

  • ?小提醒:componentDidMountcomponentWillUnmount 都是 class component 內建的lifecycle 函式。React 會在對應 component 的生命週期去呼叫其 lifecycle 函式。更多的內容將在往後的章節中介紹。

恭喜,在這個範例中已經成功完成時鐘 Component 了!讀者有興趣的話也可以在 CodePen 上試試。

updater 參數

接著我們來了解 updater 這個參數的使用情境與語法:

setState(updater[, callback])

updater 代表產生新的 state object 的 function,此 function 接受兩個參數:

  • state:當前的 state
  • props:當前的 props

updater 的使用時機是在需要由當前的 state 與 props 產出新的 State 時使用。

同樣的, updater 產出的新的 state object 包含要更新的 state property 即可,不用將舊有的 state property 全部帶入。React 會自動幫我們把新的 state 與舊的 state Shallow Merge

updater 參數範例

雖然在介紹 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 的語法:

  • 初始 state:在 class component 的 constructor 中初始化 this.state
  • 更新 state:在 class component 的 lifecycle 函式 中加上 this.setState 設定新的 state

下一章節中將更深入的介紹 State 的一些特性以及內部運作原理。

參考資料


上一篇
I Want To Know React - 初探 State
下一篇
I Want To Know React - State 內部運作原理
系列文
I Want To Know React30

1 則留言

0
Dennis Zheng
iT邦新手 5 級 ‧ 2020-12-15 23:21:22

想問下 選擇寫class component的人會比較少嗎 因為比較喜歡class的寫法!!!

目前社群主流感覺是比較偏向 function components + hooks 的寫法。

但個人認為如果自己跟團隊都能接受 class component 的話,使用 class component 語法也沒有什麼不好~

我要留言

立即登入留言