React.Componentは、Reactで継承を使用してコンポーネントを作成するためのベースクラスです。バージョン16.8でフックが導入される前は、クラスコンポーネントが状態とライフサイクルを管理する唯一の方法でした。React, 2024によると、クラスコンポーネントは引き続きサポートされていますが、新しいプロジェクトではフックを備えた関数コンポーネントが推奨されています。
重要ポイント
React.Componentは、状態、props、コンポーネントのライフサイクルを扱うための組み込みメソッドを提供する抽象クラスです。クラスコンポーネントを作成するには、React.Componentを拡張するクラスを定義し、renderメソッドを実装する必要があります。
React.Componentは最初のバージョンのReact(2013)で登場し、5年間にわたってコンポーネントを作成する主要な方法であり続けました。Reactのエコシステムはクラスを中心に構築されました:高階コンポーネント(HOC)、render props、コンテキスト — これらすべてのパターンはクラス構文を使用して実装されました。
2019年のReact 16.8のリリースに伴い、Reactチームはクラスに代わるものとしてフックを導入しました。フックは同じ問題 — 状態、副作用、コンテキスト — を解決しましたが、継承や複雑なコンテキストバインドは必要ありませんでした。React.Componentは完全にサポートされ続けていますが、新しいプロジェクトでは関数型アプローチが推奨されています。
最小限のクラスコンポーネントは、React要素を返す単一のrenderメソッドを持つクラスです。renderは唯一の必須メソッドです。これはthis.propsとthis.stateの純粋な関数でなければなりません:同じpropsと状態のセットは同じ結果を生成します。
import React, { Component } from 'react';
class Welcome extends Component {
render() {
return <h1>Hello, {this.props.name}</h1>;
}
}
コンストラクタはマウント前に実行されます。親クラスにpropsを渡すためにsuper(props)を呼び出し、this.stateを初期化します。コンストラクタを他の目的 — サブスクリプション、API呼び出し — に使用することは推奨されません。そのためにはcomponentDidMountがあります。
class Counter extends Component {
constructor(props) {
super(props);
this.state = { count: 0 };
}
render() {
return <p>Count: {this.state.count}</p>;
}
}
this.stateはコンポーネントのデータを含むオブジェクトです。直接変更する(this.state.count = 1)ことは禁止されています — 再レンダリングがトリガーされず、予測可能性が損なわれます。状態はthis.setStateを介して更新する必要があります。これにより新しい状態オブジェクトが作成され、再レンダリングがトリガーされます。
setStateはオブジェクトまたは関数を受け取ることができます。新しい状態が前の状態に依存する場合は、関数形式が推奨されます:this.setState(prev => ({ count: prev.count + 1 }))。Reactはパフォーマンス最適化のために複数のsetState呼び出しを1つのバッチにグループ化できます。
class Toggle extends Component {
constructor(props) {
super(props);
this.state = { isOn: false };
}
handleClick() {
this.setState(prev => ({ isOn: !prev.isOn }));
}
render() {
return (
<button onClick={() => this.handleClick()}>
{this.state.isOn ? 'ON' : 'OFF'}
</button>
);
}
}
setStateの重要な特性は、それが非同期であることです。setStateを呼び出した後、状態はすぐには更新されません。更新後にアクションを実行する必要がある場合は、コールバックを第2引数として渡します:this.setState(newState, () => console.log(this.state))。
クラスコンポーネントには、Reactが特定のタイミングで呼び出す定義済みのメソッドがあります。マウント — constructor → render → componentDidMount。更新 — render → componentDidUpdate。アンマウント — componentWillUnmount。
| メソッド | フェーズ | 目的 |
|---|---|---|
| constructor | マウント | 状態の初期化とメソッドのバインド |
| render | 毎回 | React要素を返す(必須) |
| componentDidMount | マウント後 | APIリクエスト、サブスクリプション、タイマー |
| componentDidUpdate | 更新後 | props/状態の変更に反応 |
| componentWillUnmount | 削除前 | サブスクリプション、タイマー、イベントリスナーのクリーンアップ |
componentDidMountは、最初のレンダリング後の副作用のための主要な場所です。ここでHTTPリクエストを実行し、WebSocket接続を確立し、タイマーを開始します。componentWillUnmountでサブスクリプションをクリーンアップしないと、メモリリークが発生します。
class UserProfile extends Component {
componentDidMount() {
fetch(`/api/users/${this.props.userId}`)
.then(res => res.json())
.then(data => this.setState({ user: data }));
}
componentWillUnmount() {
// Cancel request on unmount
}
render() {
return <div>{this.state.user?.name}</div>;
}
}
JavaScriptでは、クラスメソッドはデフォルトでインスタンスにバインドされません。メソッドをコールバックとして渡すと — イベントハンドラに、タイマーに — thisはundefined(strictモード)またはwindow(strictモードなし)になります。バインドがないと、this.setStateはエラーをスローします。
バインドには3つの方法があります:コンストラクタでのbind、コールバックでのアロー関数、アロー関数を使ったclass field。最初の方法がReactの公式推奨です:コンストラクタで1回バインドし、レンダリングごとに関数を再作成しません。
// Method 1: bind in constructor (recommended)
class MyComponent extends Component {
constructor(props) {
super(props);
this.handleClick = this.handleClick.bind(this);
}
handleClick() {
this.setState({ clicked: true });
}
render() {
return <button onClick={this.handleClick}>Click</button>;
}
}
Class fields — 自動バインディングを備えたモダンな構文。クラスプロパティのアロー関数は、コンストラクタのスコープからthisをキャプチャします。欠点は、メソッドがプロトタイプではなく各インスタンスで作成されるため、同じタイプの数百のコンポーネントでメモリ使用量が増加することです。
クラスと関数の選択は、単なる構文の問題ではありません。フックを備えた関数コンポーネントは、クラスに固有の問題を解決します:異なるライフサイクルメソッド(componentDidMount + componentDidUpdate)でのロジックの重複、thisの複雑さ、関連するロジックを再利用可能なブロックに抽出できないこと。
関数コンポーネントはテストが容易です — 内部状態がなく(フック以前はクラスでのみ利用可能でした)、コールバックを検証するためにDOMマウントを必要としません。Reactはすべての新しいプロジェクトで関数コンポーネントを推奨しており、ドキュメントや例も関数コンポーネントで書かれています。
クラスコンポーネントは、レガシーコードを扱う場合に最適な選択肢であり続けます。プロジェクトが2019年より前に開始された場合、おそらく数千のクラスコンポーネントが含まれています。これらすべてを1つのスプリントでフックに書き換えることはリスクがあります。Reactは後方互換性を保証しています:クラスコンポーネントは削除されません。
段階的な移行はコンポーネントの分析から始まります。状態のない単純なコンポーネントが最初に移行されます — これらは簡単に関数に置き換えられます。次に — 1つまたは2つの状態フィールドを持つコンポーネントで、useStateが直接this.stateとthis.setStateを置き換えます。
複数のライフサイクルメソッドを持つ複雑なコンポーネントは、useEffectを使用して移行されます。componentDidMount + componentDidUpdateのロジックは、異なる依存関係を持つ個別のuseEffect呼び出しに分割されます。componentWillUnmountはuseEffect内のreturn関数に置き換えられます。複雑な状態ロジックにはuseReducerを使用します。
自動移行ツール — @phenomnomnominal/ts-react-class-to-hookやcodemodスクリプト — は、単純なクラスを自動的に変換できます。複雑なケースでは、移行は手作業のままであり、元のコンポーネントのロジックを理解する必要があります。
よくある質問
はい。super(props)は親のReact.Componentにpropsを渡します。この呼び出しがないと、コンストラクタ内でthis.propsはundefinedになります。renderやライフサイクルメソッドでは、superがなくてもpropsは利用可能ですが、これは実装の付随的な動作です。
Reactは、不要な再レンダリングを減らすために、複数のsetState呼び出しを1つのバッチにグループ化します。setStateが同期だった場合、各呼び出しが即座にレンダリングをトリガーし、大量更新時のパフォーマンスが低下します。
いいえ。フックは関数コンポーネントでのみ機能します。クラスコンポーネントを持つプロジェクトでは、すべての従来のパターンが利用可能です:render props、HOC、コンテキストのためのConsumer。フックへの移行には、クラスを関数に変換する必要があります。
React.PureComponentは、propsと状態の浅い比較でshouldComponentUpdateを実装します。setStateで常に再レンダリングするComponentとは異なり、PureComponentはデータが変更されていない場合に再レンダリングをスキップします。これは複雑なネストのないコンポーネントの最適化です。
componentDidCatchとgetDerivedStateFromErrorを使用します。componentDidCatch(error, info)はエラーをログに記録します。getDerivedStateFromErrorはフォールバックUIを表示するために状態を更新します。これらのメソッドはクラスコンポーネントでのみ機能します — 関数の場合はラッパーのErrorBoundaryコンポーネントが必要です。
まとめ
ターンキー方式のモバイルアプリケーションを開発します
IT Sectrは2017年からスタートアップや企業向けにiOS・Androidアプリケーションを開発しています。私たちがご相談に乗り、最適なソリューションをご提案します。