Shoeisha Technology Media

CodeZine(コードジン)

記事種別から探す

Reactコンポーネントへの理解を深める

基礎からはじめるReact入門 第2回

  • LINEで送る
  • このエントリーをはてなブックマークに追加
2017/02/09 14:00

 Reactはコンポーネントを組み合わせて開発を行うことで、保守性の高いアプリケーションを実現できるライブラリです。同時に、優れたUIを提供するライブラリでもあります。本稿ではコンポーネントを使いこなすためには避けて通れない、PropsやState、およびライフサイクルについて解説します。

目次

対象読者

  • JavaScriptとWeb開発の基礎に理解がある方
  • Reactに興味/関心があり、これから学び始める方

前提環境

  • macOS Sierra 10.12
  • Node.js v6.6.0/npm 3.10.3
  • React 15.4.0

PropsとState

 Reactコンポーネント内では、PropsとStateという2つのオブジェクトが利用されます。Propsは、コンポーネントを生成するときに親から渡されるオブジェクトで、コンポーネントが画面から取り除かれるまで、不変の値を保持します。対してStateはコンポーネント内で保持される、プライベートなオブジェクトで、可変の変数を保持します。

 以下のサンプルは、180秒間のカウントダウンタイマーです。こちらでPropsとStateの使い分けのイメージを説明します。

リスト1 TimerApp/src/index.js
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';

// Reactコンポーネントクラス「Timer」を宣言
class Timer extends React.Component {
  constructor(props) {                                  // (4)
    super(props);             
    this.state = {remaining : this.props.seconds};      // (2)
  }

  // state.remainingが正の数なら1秒減じる関数
  countDown() {
      if(this.state.remaining > 0) {
            this.setState((prevState) => ({
                  remaining : prevState.remaining - 1       // (3)
            }));      
       }
  }

  // 初期化時に、countDownメソッドを1秒ごとに呼び出すタイマーを設定
  componentDidMount() {                                  // (5)
    this.interval = setInterval(() => this.countDown(), 1000);
  }

  // 終了処理として、タイマーをクリアする
  componentWillUnmount() {                              // (6)
    clearInterval(this.interval);
  }

  // Timerコンポーネントが描画する要素を記述
  render() {
    return (
      <div>
        <h1>Hello, {this.props.name}!</h1>
        <h2>{this.state.remaining} seconds remaining.</h2>
      </div>
    );
  }
}

// Propsを通してnameとsecondsを渡して、Timerコンポーネントを生成
const element = <Timer name="Filange" seconds={180} />;   // (1)

// index.htmlのid=‘root’をもつ要素にelementを挿入
ReactDOM.render(
  element,
  document.getElementById('root')
);

図1 実行結果
図1 実行結果

 Propsとして、ユーザー名とカウントする秒数を設定しています(1)。このように、一度コンポーネントを生成したら(今回の場合、カウントダウンをスタートしてからは)変化しないような値はPropsで扱います。一方、カウントダウン中の秒数は動的に変化し、刻々と画面に変化が反映されます。コンポーネント生成後にも動的に変化する変数はStateとして扱います(2)。本サンプルでは、コンポーネント生成時にintervalを設定して1秒ごとにcountDownメソッドを呼ぶことでstate.remainingを更新し、カウントダウンタイマーを実現しています。

State更新時の注意点

 Stateの更新について補足します。直接Stateを更新することはNGです(3)。下記のNGコードのように直接更新した場合、Viewに更新が反映されませんので必ずthis.setState()を呼ぶようにしてください。

// NGコード
this.state.name = ‘Ken’;

// 正しいコード
this.setState({ name : ‘Ken’ });

 また、setState()メソッドはアロー演算子を使うことで、第1引数に前の状態のstate、および第2引数にpropsを受け取ることができます。このメソッドは非同期に処理が行われるため、更新がうまく画面に反映されないような場合は、その点を疑ってみるといいかもしれません。

+++スクリプト+++
this.setState((prevState, props) => ({
  currentTime: props.format + prevState.date
}));

コンポーネントのライフサイクル

 Reactコンポーネントのクラス型で宣言する場合、必ずReact.Componentクラスを継承します。React.Componentクラスは抽象クラスであり、継承したサブクラスはライフサイクルメソッドを利用することができるようになります。前節のサンプルでもconstructorやcomponentDidMountといったメソッドを特に説明なく利用していましたが、これらはReactComponentクラスが規定するライフサイクルメソッドです。下図はメソッドの一覧を表しています。

図2 コンポーネントライフサイクルの各フェーズと対応メソッド
図2 コンポーネントライフサイクルの各フェーズと対応メソッド

 Reactコンポーネントのライフサイクルには、大きく分けて3つのフェーズがあります。コンポーネントが生成されてDOMに要素が挿入される時(Mountingフェーズ)、PropsやStateの値に変更が発生した時(Updatingフェーズ)、コンポーネントがDOMから取り除かれる時(Unmountingフェーズ)です。そして、各フェーズ内で、図の上から順番にメソッドが呼ばれます。

 前節において、constructor内でpropsを受け取ってpropsとstateを初期化する処理を行っていましたが、これはコンポーネント生成時の最初に呼ばれるライフサイクルメソッドによるものでした(4)。さらにDOM挿入が完了したタイミングでcomponentDidMountが呼ばれてタイマーを設定し(5)、DOMから取り除かれる際の処理としてタイマーをクリアしています(6)。このようにイベントの設定と解除をcomponentDidMountとcomponentWillUnmountで行うことはよくある実装なので、一つの型として覚えておいてもいいかもしれません。

表1 ライフサイクルメソッド一覧
# メソッド名 引数 概要
1 render() - React.Componentクラスに必須のメソッド。単一のReact要素を返す役割をもつが、何も描画する必要が無い場合にはnullやfalseを返すこともできる。
2 constructor() - DOM挿入前の初期化時に呼ばれる。
3 componentWillMount() - renderメソッドによるDOM挿入直前に呼ばれる。(Mountingフェーズ)
4 componentDidMount() - renderメソッドによるDOM挿入直後に呼ばれる。(Mountingフェーズ)
5 componentWillReceiveProps() nextProps 親コンポーネントから新しいpropsを受け取ったときに呼ばれる。propsが更新されたときのみに呼ばれ、stateが更新されただけでは呼ばれることはない。
6 shouldComponentUpdate() nextProps,nextState propsかstateが更新されたときに呼ばれ、初回の描画時(Mountingフェーズ)では呼ばれない。
7 componentWillUpdate() nextProps,nextState renderメソッドによるDOM挿入直前に呼ばれる。(Updatingフェーズ)
8 componentDidUpdate() prevProps,prevState renderメソッドによるDOM挿入直後に呼ばれる。(Updatingフェーズ)
9 componentWillUnmount() - React要素がDOMから取り除かれるときに呼ばれる。

 上の表はReactコンポーネントの各ライフサイクルメソッドを一覧化したものです。全てのメソッドの詳細は割愛させていただきますが、重要なポイントとしていくつか説明します。全メソッドの詳細が知りたい方は公式リファレンスをご参照ください。

 まず、[2]constructorはコンポーネントとしてメソッドとして必須ではありませんが、propsを扱う場合には必須のメソッドです。というのも下記コードのように、constructor内でsuper(props)を呼んで初期化する必要があるからです。また、constructor内でthis.propsにアクセスするとundefinedとなるため、propsはconstructorよりも後の処理で扱います。

constructor(props) {
  super(props);
  this.state = {
    date : new Date()
  };
}

 次に[5]から[8]のUpdatingフェーズで呼ばれる各メソッドについてですが、それぞれがこれから保持することになるPropsとState、または直前まで保持されていたPropsとStateを引数としてとります。PropsとStateの前後関係を比較してロジックを組むことができます。例えば、[5]componentWillReceivePropsではPropsの値に変化がなかったとしても、親コンポーネントからPropsを受け取った場合には必ず呼ばれるため、Propsの中身を前後で比較して、値に変化がない場合には更新しないといった具合です。

 また、Reactコンポーネントでは親のコンポーネントが更新されるとその子のコンポーネントも全て更新処理が走ります。shouldComponentUpdate内で、falseを返すことでその処理をキャンセルできることも覚えておくといいと思います(デフォルトではtrueが返る)。この処理に関してはjQueryのstopPropagation()の逆をイメージすると分かりやすいかもしれません。


  • LINEで送る
  • このエントリーをはてなブックマークに追加

著者プロフィール

  • WINGSプロジェクト 飯田 勝也(イイダ カツヤ)

    <WINGSプロジェクトについて> 有限会社 WINGSプロジェクトが運営する、テクニカル執筆コミュニティ(代表 山田祥寛)。主にWeb開発分野の書籍/記事執筆、翻訳、講演等を幅広く手がける。2012年2月時点での登録メンバは37名で、現在も執筆メンバを募集中。興味のある方は、どしどし応募頂きたい...

  • 山田 祥寛(ヤマダ ヨシヒロ)

    静岡県榛原町生まれ。一橋大学経済学部卒業後、NECにてシステム企画業務に携わるが、2003年4月に念願かなってフリーライターに転身。Microsoft MVP for ASP/ASP.NET。執筆コミュニティ「WINGSプロジェクト」代表。 主な著書に「入門シリーズ(サーバサイドAjax/XMLD...

バックナンバー

連載:基礎からはじめるReact入門
All contents copyright © 2006-2017 Shoeisha Co., Ltd. All rights reserved. ver.1.5