React コンポーネントの状態とイベント処理

ここでは、ボタンをクリックしたときのイベント処理について説明します。

具体例として、次のような例を考えます。

次のような青いボタンを用意します。

ボタンには数字が書いてあり、ボタンを1回クリックする度に、数字が増えていきます。

ポイントは3点あります。

ボタンがクリックされた回数を保持しているという点、 クリックイベントに応答している点、そして、表示を書き換えているという点の3点です。

React ではこうした処理をどのように行えばよいでしょうか。

先にコードを示します。このボタンのコード CountButton.js です。これをみながら説明していきます。

import React from 'react';

export default class CountButton extends React.Component {

  constructor(props){
    super(props);
    console.log('constructor');
    this.state = { count: 0 };
    this.onClick = this.onClick.bind(this);
  }

  onClick(){
    console.log('onClick');
    this.setState((prevState, props) => {
      console.log('setState | prevState.count = ' + prevState.count);
      return { count: ++prevState.count }
    });
  }
  
  componentWillMount(){
    console.log('componentWillMount');
  }

  componentDidMount(){
    console.log('componentDidMount');
  }

  componentWillUnmount(){
    console.log('componentWillUnmount');
  }

  componentWillUpdate(nextProps, nextState){
    console.log('componentWillUpdate');
    console.log(nextState);
  }

  componentDidUpdate(prevProps, prevState){
    console.log('componentDidUpdate');
    console.log(prevState);
  }

  componentWillReceiveProps(nextProps){
    console.log('componentWillReceiveProps');
  }

  shouldComponentUpdate(nextProps, nextState){
    console.log('shouldComponentUpdate');
    return true;
  }

  render() {
    console.log('render');
    var buttonStyle = {
      width: 100,
      height: 100,
      backgroundColor: "#3F51B5",
      color: "rgba(255,255,255,.87)",
      border: "none",
      borderRadius: 20,
      fontSize: 48,
      fontWeight: "bold"
    };
    return (
      <button style={buttonStyle} onClick={this.onClick}>
        {this.state.count}
      </button>
    );
  }
}

このボタンを表示するとコンソールには次のように表示されます。

実行直後には、「React コンポーネントのライフサイクルイベント その1」 でみた通り、constructor の後に componentWillMountrendercomponentDidMount の 流れが確認できます。

コンストラクタでしていること

それでは、constructor をみてみましょう。

  constructor(props){
    super(props);
    console.log('constructor');
    this.state = { count: 0 };
    this.onClick = this.onClick.bind(this);
  }

コンストラクタではプロパティを受取ります。これを super で基底クラスのコンストラクタに渡します。これはお約束です。

その次は this.state{ count: 0 } という状態 (state) を設定しています。

React コンポーネントは state というメンバーを持っています。 これが状態を保持する場所になります。

この state メンバーをコンストラクタで初期化しているのです。

それから、あとで見るように ボタンのクリックイベントハンドラとして、この CountButton クラス内の onClick メソッドを指定します。

一般的に、ES6 クラスのメソッドは既定ではイベントハンドラにバインドされません。ES6 クラスのメソッドをイベントハンドラに指定するためには、ここで行っているように、 this.メソッド = this.メソッド.bind(this) とする必要があります。

foo を関数オブジェクトのとき、foo.bind(this) とすると、bind メソッドは this を受取る新しいバウンド関数 (BF) を生成します。

BF はエキゾチック関数オブジェクトといって、元の関数をラップして、内部にある元の関数を呼び出すオブジェクトです。

render メソッドでイベントハンドラを指定した JSX を返す

コンストラクタの他では、 render メソッドでひと仕事をしてますので、みてみましょう。

ボタンのスタイル指定はとりあえず置いておいて、イベント処理に関わるポイントは button 要素の onClick={this.onClick} の箇所です。

  render() {
    console.log('render');
    var buttonStyle = {
      width: 100,
      height: 100,
      backgroundColor: "#3F51B5",
      color: "rgba(255,255,255,.87)",
      border: "none",
      borderRadius: 20,
      fontSize: 48,
      fontWeight: "bold"
    };
    return (
      <button style={buttonStyle} onClick={this.onClick}>
        {this.state.count}
      </button>
    );
  }

これによってボタンのクリックイベントで、このクラス内のメソッド onClick (すなわち this.onClick) がコールバックされるようになります。

{this.state.count} はボタンに表示する文字です。クラスの state 内の count の値を表示しています。

以上で、ボタンのクリックイベントのハンドラ設定、及び、状態の表示をする準備ができました。

次にボタンをクリックして、状態を更新して、それを表示に反映させる箇所をみてみましょう。

クリックしたときに行っていること

上記の設定によりボタンをクリックした時に、this.onClick がコールバックされます。

ここでは this.setState メソッドを呼び出しています。これは React.Component のメソッドで、this.state はここで更新します。

this.setStateアップデータメソッドを受取ります。

アップデータメソッドは (prevState, props) => (新しい状態) という形をしています。現在の状態 (つまり this.state) を prevState に受取り、新しく this.state にセットするオブジェクトを返せば良いです。

今回の例では次のように書いています。

onClick(){
  console.log('onClick');
  this.setState((prevState, props) => {
    console.log('setState | prevState.count = ' + prevState.count);
    return { count: ++prevState.count }
  });
}

形的には console.log を追加したかったので、 (prevState, props) => (新しい状態) ではなく、(prevState, props) => { ...; return 新しい状態} としています。

表示の更新

さて、クリックハンドラで this.setState を呼ぶことにより、状態を更新しました。

その結果、コンポーネントに追加の更新処理が必要となる場合どうしたらよいでしょうか。

今回の場合は、ボタンのラベルを更新したいところです。

this.setState を呼ぶと、React のライフサイクルイベントの一つとして shouldComponentUpdate メソッドがコールバックされます。

ここで true を返せば、さらなる更新処理が必要であると指定できます。false を返せば更新処理は行いません。

今回は表示を更新したいので、true を返しています。

  shouldComponentUpdate(nextProps, nextState){
    console.log('shouldComponentUpdate');
    return true;
  }

すると、shouldComponentUpdate に続き、componentWillUpdaterendercomponentDidUpdate が呼び出されます。

この流れで必要な処理を実装すれば OK です。

今回は画面の表示を変えるだけなので、render が呼ばれれば十分です。

この流れで、ボタンをクリックするとクリックした回数を表示できています。

以上で、状態の保持、状態の更新、イベント処理、それぞれの方法について説明しました。

ここまでお読みいただき、誠にありがとうございます。SNS 等でこの記事をシェアしていただけますと、大変励みになります。どうぞよろしくお願いします。

© 2024 React 入門