React 速習チュートリアル

React クラスコンポーネント

React 16.8 以前は、React コンポーネントで State(ステート)Lifecycle(ライフサイクル) を管理する唯一の方法はクラスコンポーネントでした。当時、関数コンポーネントは「ステートレス(状態を持たない)」ものと見なされていました。

Hooks(フック) の導入により、現在では関数コンポーネントはクラスコンポーネントとほぼ同等の機能を持ちます。その差は非常にわずかであり、現在の React 開発においてクラスコンポーネントを新しく作成する必要性はほとんどありません。

関数コンポーネントが推奨されていますが、React からクラスコンポーネントを削除する計画は現時点ではありません。

このセクションでは、React におけるクラスコンポーネントの使用方法の概要を説明します。必要に応じてこのセクションをスキップし、関数コンポーネントの学習に進んでも構いません。

1. クラスコンポーネントの作成

React コンポーネントを作成する際、コンポーネント名は必ず大文字で始める必要があります。

クラスコンポーネントは extends React.Component を含める必要があります。この記述により React.Component を継承し、コンポーネントは React.Component の持つさまざまな機能にアクセスできるようになります。

また、クラスコンポーネントには HTML を返す render() メソッドが必須です。

1.1 Car クラスコンポーネントの作成例

class Car extends React.Component {
  render() {
    return <h2>こんにちは、私は車です!</h2>;
  }
}

これで、<h2> 要素を返す Car というコンポーネントが作成されました。
アプリケーション内でこのコンポーネントを使用するには、通常の HTML と同様の構文 <Car /> を使用します。

1.2 コンポーネントのレンダリング

createRoot(document.getElementById('root')).render(
  <Car />
);

2. コンポーネントのコンストラクタ (Constructor)

コンポーネント内に constructor() 関数がある場合、その関数はコンポーネントが初期化される際に呼び出されます。
Constructor(コンストラクタ) は、コンポーネントのプロパティを初期化する場所です。

React では、コンポーネントのプロパティは state という名前のオブジェクト内に保持する必要があります。state については、このチュートリアルの後半で詳しく説明します。

また、コンストラクタ内では super() ステートメントを含めることで、親コンポーネント(React.Component)の継承を遵守します。これにより親のコンストラクタ関数が実行され、コンポーネントは React.Component のすべての機能を使用できるようになります。

2.1 コンストラクタでカラープロパティを追加する例

class Car extends React.Component {
  constructor() {
    super();
    this.state = { color: "赤" };
  }
  render() {
    return <h2>私は車です!</h2>;
  }
}

2.2 render 関数内で state の値を使用する

class Car extends React.Component {
  constructor() {
    super();
    this.state = { color: "赤" };
  }
  render() {
    return <h2>私は {this.state.color} の車です!</h2>;
  }
}

3. プロップス (Props)

コンポーネントのプロパティを扱うもう一つの方法が Props(プロップス) です。
Props は関数の引数のようなもので、HTML の属性としてコンポーネントに渡されます。

3.1 属性を介して Props を渡す例

class Car extends React.Component {
  render() {
    return <h2>私は {this.props.color} の車です!</h2>;
  }
}

createRoot(document.getElementById('root')).render(
  <Car color="赤"/>
);

3.2 コンストラクタ内での Props

コンポーネントにコンストラクタ関数がある場合、Props は常にコンストラクタに渡され、さらに super(props) メソッドを介して React.Component に渡される必要があります。

class Car extends React.Component {
  constructor(props) {
    super(props);
  }
  render() {
    return <h2>私は {this.props.model} です!</h2>;
  }
}

createRoot(document.getElementById('root')).render(
  <Car model="マスタング"/>
);

4. コンポーネントのネストとファイル分割

他のコンポーネントの中でコンポーネントを参照したり、ファイルを分割して管理したりすることができます。

4.1 コンポーネント内のコンポーネント

class Car extends React.Component {
  render() {
    return <h2>私は車です!</h2>;
  }
}

class Garage extends React.Component {
  render() {
    return (
      <div>
        <h1>私のガレージには誰が住んでいる?</h1>
        <Car />
      </div>
    );
  }
}

4.2 コンポーネントの外部ファイル化

再利用性を高めるため、コンポーネントを別のファイルに保存します。ファイル拡張子は .jsx とし、先頭で React をインポートし、末尾で export default を記述します。

Vehicle.jsx

import React from 'react';

class Car extends React.Component {
  render() {
    return <h2>こんにちは、私は車です!</h2>;
  }
}

export default Car;

main.jsx(インポートして使用)

import { createRoot } from 'react-dom/client'
import Car from './Vehicle.jsx';

createRoot(document.getElementById('root')).render(
  <Car />
);

5. クラスコンポーネントの State(状態管理)

React のクラスコンポーネントには、組み込みの state オブジェクトがあります。state オブジェクトは、そのコンポーネントに属するプロパティ値を保存する場所です。
state オブジェクトが変更されると、コンポーネントは再レンダリング(Re-render)されます。

5.1 state オブジェクトの初期化

state はコンストラクタ内で初期化します。

class Car extends React.Component {
  constructor(props) {
    super(props);
    this.state = { brand: "フォード" };
  }
  render() {
    return (
      <div>
        <h1>私の車</h1>
      </div>
    );
  }
}

5.2 複数のプロパティを持つ state

class Car extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      brand: "フォード",
      model: "マスタング",
      color: "赤",
      year: 1964
    };
  }
  render() {
    return (
      <div>
        <h1>私の {this.state.brand}</h1>
        <p>
          これは {this.state.year} 年製の {this.state.color} の
          {this.state.model} です。
        </p>
      </div>
    );
  }
}

5.3 state オブジェクトの変更

state オブジェクト内の値を変更するには、this.setState() メソッドを使用します。
値が変更されると、コンポーネントは再レンダリングされ、出力が新しい値に基づいて更新されます。

class Car extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      brand: "フォード",
      model: "マスタング",
      color: "赤",
      year: 1964
    };
  }
  changeColor = () => {
    this.setState({ color: "青" });
  }
  render() {
    return (
      <div>
        <h1>私の {this.state.brand}</h1>
        <p>
          色は {this.state.color} です。
        </p>
        <button
          type="button"
          onClick={this.changeColor}
        >色を変更する</button>
      </div>
    );
  }
}

       重要:state オブジェクトを変更する際は必ず setState() を使用してください。これにより、React が更新を検知し、render() メソッド(および他のライフサイクルメソッド)を呼び出すことが保証されます。

6. コンポーネントのライフサイクル (Lifecycle)

React の各コンポーネントには、3 つの主要なフェーズで監視および操作できる Lifecycle(ライフサイクル) があります。
3 つのフェーズとは、Mounting(マウンティング)Updating(アップデーティング)Unmounting(アンマウンティング) です。

6.1 Mounting (マウンティング)

Mounting とは、要素を DOM に配置することを意味します。
コンポーネントがマウントされる際、React は以下の 4 つのメソッドをこの順序で呼び出します。

  1. constructor()
  2. getDerivedStateFromProps()
  3. render()
  4. componentDidMount()

6.1.1 constructor

コンポーネントが初期化される際、最初に呼び出されます。初期 state やその他の初期値を設定するのに適した場所です。必ず最初に super(props) を呼び出してください。

6.1.2 getDerivedStateFromProps

要素を DOM にレンダリングする直前に呼び出されます。Props に基づいて state を設定する場合に使用します。

6.1.3 render

必須のメソッドであり、実際に HTML を DOM に出力します。

6.1.4 componentDidMount

コンポーネントがレンダリングされた直後に呼び出されます。すでに要素が DOM に配置されている必要がある処理(API 呼び出しやタイマー設定など)を実行します。

class Header extends React.Component {
  constructor(props) {
    super(props);
    this.state = { favoritecolor: "赤" };
  }
  componentDidMount() {
    setTimeout(() => {
      this.setState({ favoritecolor: "黄色" })
    }, 1000)
  }
  render() {
    return (
      <h1>私の好きな色は {this.state.favoritecolor} です</h1>
    );
  }
}

6.2 Updating (アップデーティング)

コンポーネントの stateprops に変更があったとき、Updating フェーズが発生します。
更新時には以下の 5 つのメソッドが順に呼び出されます。

  1. getDerivedStateFromProps()
  2. shouldComponentUpdate()
  3. render()
  4. getSnapshotBeforeUpdate()
  5. componentDidUpdate()

6.2.1 shouldComponentUpdate

React がレンダリングを続行すべきかどうかの真偽値(Boolean)を返します。デフォルトは true です。

6.2.2 getSnapshotBeforeUpdate

更新直前の propsstate にアクセスできます。更新後でも、更新前の値を確認することが可能です。これを使用する場合は componentDidUpdate() も定義する必要があります。

6.2.3 componentDidUpdate

コンポーネントが DOM 内で更新された直後に呼び出されます。

class Header extends React.Component {
  constructor(props) {
    super(props);
    this.state = { favoritecolor: "赤" };
  }
  componentDidMount() {
    setTimeout(() => {
      this.setState({ favoritecolor: "黄色" })
    }, 1000)
  }
  getSnapshotBeforeUpdate(prevProps, prevState) {
    document.getElementById("div1").innerHTML =
    "更新前のお気に入りは " + prevState.favoritecolor + " でした";
  }
  componentDidUpdate() {
    document.getElementById("div2").innerHTML =
    "更新後のお気に入りは " + this.state.favoritecolor + " です";
  }
  render() {
    return (
      <div>
        <h1>私の好きな色は {this.state.favoritecolor} です</h1>
        <div id="div1"></div>
        <div id="div2"></div>
      </div>
    );
  }
}