はじめてのReact #4「Lifecycleを活用してみる」

ComponentのLifecycle

ReactのComponentは生まれてから死ぬ(破棄される)までの間にたくさんのイベントが用意されており、それらのタイミングで特定のメソッドを実行してくれる機能を自由に使用することができます。

現在時間を表示するComponent その3

今回はReactの公式ドキュメントのソースを参考にしながら、前回まで作成してきたJustnowComponentを時間が経過するごとに表示も変更するようにしてみたいと思います。

サンプル

<!DOCTYPE html>
<html>
<head>
  <meta charset="UTF-8" />
  <title>Justnow3</title>
  <script src="https://unpkg.com/react@16/umd/react.development.js"></script>
  <script src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>

  <!-- Don't use this in production: -->
  <script src="https://unpkg.com/babel-standalone@6.15.0/babel.min.js"></script>
</head>
<body>
  <div id="root"></div>

  <script type="text/babel">
    class Justnow extends React.Component {
      constructor(props) {
        super(props);
        this.state = {
          now: new Date()
        };
      }

     /**
      * Lifecycle: ComponentがDOMツリーに加わった際の処理
      */
      componentDidMount() {
        this.timerID = setInterval(
          () => this.tick(),
          1000
        );
      }

     /**
      * Lifecycle: ComponentがDOMツリーから削除される際の処理
      */
      componentWillUnmount() {
        clearInterval(this.timerID);
      }

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

      render() {
        let now = this.state.now;
        let now_date = [now.getFullYear(), now.getMonth()+1, now.getDate()].join('-');
        let now_time = [now.getHours(), now.getMinutes(), now.getSeconds()].join(':');

        return <h1>{now_date} {now_time}</h1>;
      }
    }

    ReactDOM.render(
      <Justnow />,
      document.getElementById('root')
    );
  </script>
</body>
</html>

以下のページにあるサンプルコードを参考にしています。 reactjs.org

実行結果

動きのあるComponentになりましたねー。

解説

Lifecycleの一覧

Lifecycleは大きくComponentが追加される前の処理、Componentが更新される際の処理、最後に破棄される際の処理に別れています。それぞれ見ていきます。これらはReactのバージョンが上がると変化する場合があるので、常に最新のドキュメントで確認してからご利用ください。このバージョンが上がるというのはメジャーバージョンアップ以外でも起こりますのでご注意を。

マウンティング

class Foo extends React.Component {
  constructor(props) {
    // 一番最初に実行されます。
  }
  componentWillMount(){
    // ComponentがDOMツリーに追加される直前に実行されます(まだ追加されていない点に注意)。初期化処理などを行うための物。
    // 通常は constructor() でやれば良い気もする
  }
  render(){
    // 描画処理
  }
  componentDidMount(){
    // ComponentがDOMツリーに追加された直後(render()実行直後)に実行されます。
    // DOMツリーに追加された要素などに対して処理を行うことが可能です。
  }
}

更新時

class Foo extends React.Component {
  componentWillReceiveProps(nextProps){
    // Propが更新される度に呼び出されます。引数の`nextProps`に最新のPropsがもらえます。
  }
  shouldComponentUpdate(nextProps, nextState){
    // PropsやStateが更新された際に呼び出されます。
    // 後続処理である`componentWillUpdate()`と`render()`を実行するかどうかを判断するためのメソッドです。
    // `false`を返却した場合は後続処理が実行されず、`true`が返されると実行されます。
    // 無駄な描画や処理を行いたくない場合などに定義します。
  }
  componentWillUpdate(nextProps, nextState){
    // `shouldComponentUpdate()`でtrueが返されたか、もしくは定義されていない場合に実行されます。
    // `render()`が実行される前に呼ばれる最後のメソッドです。
    // このメソッド内でPropsやStateを更新すると`shouldComponentUpdate()`が呼ばれ、無限ループに突入します
  }
  render(){
    // 描画処理
  }
  componentDidUpdate(nextProps, nextState){
    // render()実行後に呼び出されます。
  }
}

破棄時

class Foo extends React.Component {
  componentWillUnmount(){
    // Componentが破棄される直前に実行されます。
  }
}

エラー発生時

バージョン16からはエラー発生時の処理も定義できるようになりました。

class Foo extends React.Component {
  componentDidCatch( error, info){
    // エラー時に実行されます。
    // componentDidCatch()自体のエラーは捕捉されないので注意。
  }
}

たくさん用意されていますが、必要のないものまで使用する(定義する)必要はもちろんありません。最初は必要なものだけ使っていきましょう。

Stateの更新

今回の見所はLifecycleと合わせてこちらのStateの更新方法でしょう。

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

このようにStateの値を更新する際には必ずthis.setState()メソッドを通して実行する必要があります。このメソッドを通すことにより自動的にrender()メソッドが実行されます。  ※例外としてコンストラクタでは利用してはいけません。

ただ一点気になるのは、次のようにコンストラクタで複数のStateを定義した場合、this.setState()時にmessageが削除されてしまうのでは?と心配になりますが、そこのあたりはうまくできており連想配列(ハッシュ)で指定した項目だけが上書きされるようになっています。ご安心を。

class Justnow extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      now: new Date(),
      message: "現在の時間は"  //★追加★
    };
  }

  /*-- 中略 --*/

  tick() {
    this.setState({     //Stateがまるごとこのハッシュに置き換わっちゃう??
      now: new Date()
    });
  }

  /*-- 中略 --*/

  render() {
    let now = this.state.now;
    let now_date = [now.getFullYear(), now.getMonth()+1, now.getDate()].join('-');
    let now_time = [now.getHours(), now.getMinutes(), now.getSeconds()].join(':');

    // 以下のように実際に表示して確認してみるとわかります
    return <h1>{this.state.message} {now_date} {now_time}</h1>;
  }
}

書籍

Lifecycleの一覧についてはこちらの情報を元にさせていただきました。 4章「Reactコンポーネント」 4.3ライフサイクル より

React入門 React・Reduxの導入からサーバサイドレンダリングによるUXの向上まで (NEXT ONE)
穴井 宏幸 石井 直矢 柴田 和祈 三宮 肇
翔泳社
売り上げランキング: 139,067

参考ページ

reactjs.org reactjs.org