Reactのクラスコンポーネントと関数コンポーネントの違い

Reactではコンポーネントの定義を記述する方法として以下の2つがあります。

・Class Component(クラスコンポーネント)

・Function Component(関数コンポーネント)

React Hooksが導入されるVer16.8以前は関数コンポーネントにはstate(状態)を持たせる処理を行うことができなかったので、クラスコンポーネントを使うことが主流でしたが、現在ではHooksが導入されたことにより、関数コンポーネントでもstateやライフサイクルに相当するものが実装できるようになっていることもあり、React公式では関数コンポーネントを使うことを推奨しているようです。

今回の記事ではクラスコンポーネントと関数コンポーネントの違いと使い分けについて解説していきます。

クラスコンポーネント(Class Component)

クラスコンポーネントの基本コード

import React from "react";

class ClassComponent extends React.Component {
  render() {
    return <div>This is Class Component</div>;
  }
}

React.Componentを継承してrenderメソッドでJSXをリターンする構文で記述します。

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

Reactのコンポーネントにはライフサイクルという概念があります。React.Componentを拡張したクラスコンポーネントでは、予め用意されているrender()メソッド等を利用することでライフサイクルの中で任意のタイミングで処理を実行することができます。

公式サイトにて説明されているライフサイクルの図

ライフサイクルには大きく分けて下記の3つの期間が設定されています。

1.Mounting(マウント時)
コンポーネントのHTMLがレンダリングされるまでの期間

2.Updating(更新時)
コンポーネントのHTMLがレンダリングされた後の期間

3.Unmounting(マウント解除時)
現在表示されているコンポーネントから他のコンポーネントへ切り替える前に、現在のコンポーネントを破棄するタイミングの期間

ライフサイクルメソッド

上記のライフサイクルにはそれぞれ付随したライフサイクルメソッドというものがあり、ライフサイクルの処理の順番で実行されます。

Mounting(マウント時) に実行されるライフサイクルメソッド

constructor()

Mounting時に一番最初に呼ばれるメソッドです

getDerivedFromProps()

render()が呼ばれる前にstateの更新があるかどうかを確認します

render()

ReactがJSXコードを解析して仮想DOMを構築します

componentDidMount()

1回目のrender()が呼ばれた時に1度だけ呼ばれます

Updating(更新時) に実行されるライフサイクルメソッド

getDerivedStateFromProps()

Updating 時に一番最初に呼ばれるメソッドです

shouldComponentUpdate()

コンポーネントの評価と再レンダリングを継続するかどうかを判定します

render()

ReactがJSXコードを解析して仮想DOMを構築します

getSnapshotBeforeUpdate()

Updatingが発生する直前のスクロールを位置などの値を引き継ぎます

componentDidUpdate()

Updating が完了した際に呼ばれるメソッドです

Unmounting(マウント解除時) に実行されるライフサイクルメソッド

componentWillUnmount()

コンポーネントがUnmountされるときに呼ばれるメソッドです

クラスコンポーネントを使うメリット

1.ライフサイクルで細かい処理を入れることができる

2.メソッドが多くてもコードの見通しが良い

関数コンポーネント(Function Component)

関数コンポーネントの基本コード

import React from "react";

const Component = () => {
  return <div>This is Functional Component</div>;
};

関数コンポーネントのライフサイクル(Hooks)

useState

useStateフックは、React本体に関数コンポーネント専用の保存領域を作成してもらい、そこにあるデータを読み書きできるフックです

useEffect

「副作用を実行するフック」とも呼ばれており、コンポーネントのレンダリング後に実行されます。

useMemo

useMemoフックは単純に値を保存するためのフックです。useStateと違い、更新関数はありません。

useCallback

useMemoは何でもキャッシュすることができるので、例えば関数をキャッシュすることができます。

関数コンポーネントを使うメリット

1.クラスコンポーネントに比べて簡潔に書ける

2.React.createElement()と比べると45%ほどレンダーが速い

クラスコンポーネントと関数コンポーネントの違い

クラスコンポーネントで記述した例

import React from "react";
import ReactDOM from "react-dom";
import "./styles.css";

class HelloComponent extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      name: "Hoge"
    };
  }

  render() {
    return (
      <div className="App">
        <h1>React Class Component</h1>
        <h2>
          Hello {this.state.name} ! {this.props.message}
        </h2>
        <input
          type="text"
          value={this.state.name}
          onChange={(e) =>
            this.setState({
              name: e.target.value
            })
          }
        />
      </div>
    );
  }
}

let dom = document.getElementById("root");
ReactDOM.render(<HelloComponent message="by Class" />, dom);

関数コンポーネントで記述した例

import React, { useState } from "react";
import ReactDOM from "react-dom";
import "./styles.css";

const HelloComponent = (props) => {
  const [name, setName] = useState("World");

  return (
    <div className="App">
      <h1>React Class Component</h1>
      <h2>
        Hello {name} ! {props.message}
      </h2>
      <input
        type="text"
        value={name}
        onChange={(e) => setName(e.target.value)}
      />
    </div>
  );
};

let dom = document.getElementById("root");
ReactDOM.render(<HelloComponent message="by Function" />, dom);

Propsによるデータの受け渡し

クラスコンポーネントの場合

this.props.XXXと記述することで利用可能

関数コンポーネントの場合

関数定義の引数としてpropsを定義することで利用可能

stateの初期設定

クラスコンポーネントの場合

コンストラクタ内でステートの初期設定を行います

関数コンポーネントの場合

Hooksの機能であるuseStateを使って初期設定を行います

stateの更新処理

クラスコンポーネントの場合

steteを更新するためのsetStateを使って更新します
this.setState({XXX: value})

関数コンポーネントの場合

useSteateで定義されたsetXXX関数を経由して値を変更します

stateの更新処理(差分更新)

クラスコンポーネントの場合

this.setStateがオブジェクトの場合でも、オブジェクト内の更新する変数のみ更新することが可能です

<Form.Control 
    value={this.state.name} 
    onChange={(e) => this.setState({
        name: e.target.value
    })} 
/>

関数コンポーネントの場合

useStateのsetXXXはstateがオブジェクトの場合はオブジェクト内の全ての変数を更新する記述をする必要があります。

<Form.Control 
    value={person.name} 
    onChange={(e) => setPerson({
        name: e.target.value,
        email: "",
        address: "",
        age: 0
    })} 
/>

クラスコンポーネントと関数コンポーネントの使い分け

クラスコンポーネントを使うケース

下記のケースに該当する場合はクラスコンポーネントを使うとよいでしょう。
但しクラスの場合はthisが多用されることになるので、ソース内の混乱が発生することになりがちなので、コーディングルールには気をつけたほうがいいです。

・Lifecycle methodを使う必要がある場合

・ 複雑なstateを持たせる場合

・ メソッドが多い場合

関数コンポーネントを使うケース

上記の「 クラスコンポーネントを使うケース 」以外は全て関数コンポーネントでいいと思います。
(React公式でも推奨は関数コンポーネントのほうなので)

特に渡したい引数であるpropsを渡して表示するコンポーネントであれば関数コンポーネントのほうにメリットがあります。

まとめ

関数コンポーネントの記述は短くシンプルに記述することが出来るので、アプリケーションの全体設計やメンテナンスの面では関数コンポーネントをメインで使っていくことがいいでしょう。

また、クラスコンポーネントは廃止されておらず継続的に使えますので、基本的には関数コンポーネントをメインで使いつつ、必要に応じてクラスコンポーネントを使っていくようにすると良いと思います。

参考リンク

45% Faster React Functional Components, Now

https://medium.com/missive-app/45-faster-react-functional-components-now-3509a668e69f

Classコンポーネントのライフサイクル(React公式)

https://projects.wojtekmaj.pl/react-lifecycle-methods-diagram/

Leave a Reply

Your email address will not be published. Required fields are marked *