iT邦幫忙

2017 iT 邦幫忙鐵人賽
DAY 5
0
Modern Web

寫React的那些事系列 第 5

React Day5 - state 與 setState

React中有兩個東西會控制component:props和state,props是由父元件傳入,在component中,它是不會被改變的,而要改變的資料會放在我們今天要介紹的state。

State


state比較像是component內部的變數,有私有(private)與封裝(encapsulated)的概念,只有component可以使用自己的state,有別於props是父元件傳入。

使用state有兩個重點:

  • 通常我們會在component的 constructor 設定state初始值。
  • 要改變state,必須使用setState()。

這邊引用官網的例子,實作一個時鐘來說明state用法。

執行render重繪時鐘


當我們把element render到畫面上之後,要再次更新畫面就必須重新執行ReactDOM.render(),所以這邊利用setInterval,每秒重新執行React.render()。

function tick() {
  const element = (
    <div>
      <h1>Hello, world!</h1>
      <h2>It is {new Date().toLocaleTimeString()}.</h2>
    </div>
  );
  ReactDOM.render(
    element,
    document.getElementById('root')
  );
}

// render every second
setInterval(tick, 1000);

Demo

利用props update重繪時鐘


建立component來實作Clock,並傳入一個名為date的props。利用每秒setInterval,重新傳入props給Clock,每當Clock收到props更新,就會重新render畫面。

class Clock extends React.Component {
  render() {
    return (
      <div>
        <h1>Hello, world!</h1>
        <h2>It is {this.props.date.toLocaleTimeString()}.</h2>
      </div>
    );
  };
}

function tick() {
  ReactDOM.render(
    <Clock date={new Date()} />,
    document.getElementById('root')
  );
}
setInterval(tick, 1000);

Demo

利用state update重繪時鐘


(1) 首先要先在Clock constructor建立state初始值,state是一個object,在裡面設定date變數並把目前時間指定給state。再來,把render裡面,原本從props取得的date,改成從state取date變數。

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>
    );
  };
}

(2) 移除render時,從element傳入的props,React.render也只需要執行一次。

ReactDOM.render(
    <Clock />,
    document.getElementById('root')
);

(3) 在內部update state,這步驟需要一點說明:

  • 新增內建function: componentDidMount ,在這個component第一次render完的時候,會執行這個function,mount表示顯示在DOM上(只有第一次被render出來的時候)。
  • 新增內建function: componentWillUnmount ,在這個component將要被移除在DOM上的時候,會執行這個function,unmount表示從DOM中被移除。
  • 新增自訂function: tick ,自訂的方法要注意的事情是this的使用,只有React的內建function,this會指向component本身,如果在自訂的方法裡面希望this也指向component,我們可以在constructor先設定bind把this指定給它。參考Javascript的this
  • 另一個說明,我們使用setInterval指定的timerID為什麼不設定成state,因為其實它跟render的內容並沒有關係,官網說 「If you don't use something in render(), it shouldn't be in the state.」 ,使用this.timerID也表示它是React裡面的private變數,只是因為和render無關,所以不必設成state。
  • 這個範例可以看到,date是Clock自己內部的變數,當建立多個Clock時,每個Clock各自用自己的state值render互不影響。
class Clock extends React.Component {
  constructor(props) {
    super(props);
    this.state = {date: new Date()};
    // 指定this
    this.tick = this.tick.bind(this);
  }

  componentDidMount() {
    this.timerID = setInterval(
      this.tick,
      1000
    );
  }

  componentWillUnmount() {
    clearInterval(this.timerID);
  }

  render() {
    return (
      <div>
        <h1>Hello, world!</h1>
        <h2>It is {this.state.date.toLocaleTimeString()}.</h2>
      </div>
    );
  }

  tick() {
    this.setState({
      date: new Date()
    });
  }
}

ReactDOM.render(
  <Clock />,
  document.getElementById('root')
);

Demo

最後,總結使用state要注意的事情


(1) 在ES6 class寫法中,要在constructor設定state初始值,直接指定object給state。

(2) 當要改變state時,不可以直接指定,必須使用setState(),因為setState()之後才會觸發render更新畫面。

(3) State是一個object,當使用setState()時可以update個別的key。

(4) 如果state會依據props改變,props與state的更新不一定會同步,所以使用setState()傳入的參數就不是要更新的state object,而會是一個傳入 previous stateprops 的function,並且回傳要更新的state object。下面範例使用ES6 Arrow function,直接回傳function運算後的state。

// Wrong,有可能會抓到不正確props
this.setState({
  counter: this.state.counter + this.props.increment
});

// Correct
this.setState((prevState, props) => ({
  counter: prevState.counter + props.increment
}));

Stateless component


不一定每個元件都會需要有state,可以視情況使用,如果component不需要保有自己的變數,不需控制Life Cycle,使用stateless component效能也會比較好喔!

單向資料流(Unidirectional Data Flow)


前面提到父元件傳props給子元件,子元件可以依據props設定state或是自己定義state,但state只有component自己使用,不會往上傳,所以在React裡只有單向資料流,就像waterfall一樣,永遠是"top-down"。

參考


React官網 State and Lifecycle


上一篇
React Day4 - Component 與 Props
下一篇
React Day6 - Life Cycle 生命週期
系列文
寫React的那些事31
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言