はじめに
Reactのコンポーネントは関数コンポーネントが主流になっていますが、クラスベースのReact.Componentも依然として理解しておく価値があります。特に既存の大規模プロジェクトやError Boundary(エラーバウンダリ)実装にはクラスコンポーネントが欠かせません。提供する機能を1つずつ丁寧に見ていきましょう。
React.Componentとは
React.Component は、ユーザー定義のクラスコンポーネントが継承する基底クラスです。内部では以下を提供します。
props:親から渡される読み取り専用データstate:コンポーネント内部で変化する値render():UIの描画を司る必須メソッド- ライフサイクルメソッド:マウント・更新・アンマウント時のフック
setState():state更新と再レンダーのトリガー- エラーバウンダリ:
static getDerivedStateFromError/componentDidCatch
import React from 'react';
class MyComponent extends React.Component {
constructor(props) {
super(props);
this.state = { count: 0 };
}
render() {
return <div>カウント: {this.state.count}</div>;
}
}propsの扱い
this.props には親から渡された全ての値が含まれています。コンポーネント内で変更してはいけません。
class Greeting extends React.Component {
render() {
return <h1>こんにちは、{this.props.name}さん!</h1>;
}
}
// 呼び出し側
<Greeting name="太郎" />- 型注釈:PropTypes で型・必須チェックを行う例も多い
- デフォルトProps:
MyComponent.defaultProps = { count: 10 };
stateとsetState
state はコンポーネントの内部情報を保持し、setState で更新します。setState は非同期バッチ処理されるため注意が必要です。
class Counter extends React.Component {
state = { count: 0 };
handleClick = () => {
this.setState({ count: this.state.count + 1 });
// 複数回連続呼び出すと反映タイミングがずれる可能性あり
}
render() {
return <button onClick={this.handleClick}>{this.state.count}</button>;
}
}- 関数型更新:前回のstateを確実に利用したい場合
this.setState(prev => ({ count: prev.count + 1 }));- マージ:オブジェクトをマージする形で更新される
ライフサイクルメソッド一覧
クラスコンポーネントは以下のメソッドをオーバーライドできます。
| フェーズ | メソッド | 説明 |
|---|---|---|
| マウント前 | static getDerivedStateFromProps(props, state) | Propsからstateを生成(副作用禁止) |
| マウント後 | componentDidMount() | API呼び出しやDOM操作を実行 |
| 更新判定 | shouldComponentUpdate(nextProps, nextState) | レンダーをスキップする判断 |
| 更新前 | getSnapshotBeforeUpdate(prevProps, prevState) | 更新前のDOM情報を取得(返却値は次のcomponentDidUpdateに渡る) |
| 更新後 | componentDidUpdate(prevProps, prevState, snapshot) | 更新後に副作用を実行 |
| アンマウント前 | componentWillUnmount() | タイマー解除、購読解除などのクリーンアップ |
| エラー処理 | static getDerivedStateFromError(error) | エラー発生時にstateを更新 |
componentDidCatch(error, info) | UIエラーのログ送信など |
例:データフェッチ
class DataFetcher extends React.Component {
state = { data: null };
componentDidMount() {
fetch(this.props.url)
.then(r => r.json())
.then(json => this.setState({ data: json }));
}
render() {
return this.state.data
? <div>{this.state.data.title}</div>
: <p>読み込み中…</p>;
}
}Error Boundary(エラーバウンダリ)
クラスコンポーネントのみ実装可能な仕組みで、子コンポーネントでのレンダーエラーをキャッチできます。
class ErrorBoundary extends React.Component {
state = { hasError: false };
static getDerivedStateFromError() {
return { hasError: true };
}
componentDidCatch(error, info) {
logErrorToService(error, info);
}
render() {
if (this.state.hasError) {
return <h2>エラーが発生しました</h2>;
}
return this.props.children;
}
}- 再起動:Error Boundaryをリセットするロジックを外部から制御可能
- 注意:副作用フックの代替ではない
コンテキストとの連携
static contextType = MyContext をクラスに指定すると this.context で値を参照できます。
MyComponent.contextType = ThemeContext;
class MyComponent extends React.Component {
render() {
return <div style={{ color: this.context.primary }}>…</div>;
}
}パフォーマンス最適化
shouldComponentUpdate
再レンダーを制御し、不要なrender()呼び出しを抑えます。
class PureCounter extends React.Component {
shouldComponentUpdate(nextProps) {
return nextProps.value !== this.props.value;
}
render() { /* … */ }
}PureComponent
React.PureComponent を継承すると自動的に shallow 比較で shouldComponentUpdate を実装します。
メソッドのバインディング
this の参照を安定させるため、コンストラクタでバインディングするか、クラスフィールド構文を使います。
// コンストラクタ内で
constructor(props) {
super(props);
this.handleClick = this.handleClick.bind(this);
}
// クラスフィールド
handleClick = () => { /* this が正しい */ }デフォルトPropsとdisplayName
- defaultProps
MyComponent.defaultProps = { text: 'デフォルトテキスト' };- displayName
- デバッグ時の表示名を変更できます。
MyComponent.displayName = 'カスタム名';まとめ
React.Component を理解すると、既存のクラスコンポーネントやError Boundaryが読み書きできるようになります。ライフサイクルメソッド、state / props、パフォーマンス最適化パターンなどを活用し、堅牢で読みやすいUIコンポーネントを実装しましょう。




コメント