クラスコンポーネントは、React.Component を継承し、render() メソッドからJSXを返すJavaScriptのクラスです。propsを this.props から読み、stateを this.state に持ち、this.setState() で更新します。新しいコードではフックを使った関数コンポーネントに取って代わられましたが、クラスは今も完全にサポートされ、多くの古いコードベースに登場し、エラーバウンダリを書く唯一の方法です。
それぞれのカウンターは自分のstateを持っています。3つ目の <Counter label="Plums" /> を追加すると、それも0から始まります。
クラスコンポーネントの構造
import { Component } from 'react';
class Welcome extends Component {
static defaultProps = { name: 'stranger' };
constructor(props) {
super(props); // required before using this
this.state = { visits: 1 };
}
render() {
return <h1>Hello, {this.props.name}. Visit #{this.state.visits}</h1>;
}
}
- クラスは
Component(またはimport React from 'react'でのReact.Component)を継承します。 render()は必須です。関数コンポーネントの本体と同じように純粋でなければなりません。this.propsとthis.stateを読み、JSXを返します。this.propsはpropsを持ちます。読み取り専用です。- stateは
constructorで設定する(先にsuper(props)を呼びます)か、今ではより一般的にクラスフィールドとして設定します:state = { visits: 1 };。 static defaultPropsはReact 19でもクラスでは動きます(削除されたのは関数コンポーネントでだけです)。
this.setStateはマージし、更新関数も受け取れる
setState は、useState のセッターと2つの点で違います。渡したオブジェクトを現在のstateにマージするので、名前を挙げたキーだけを更新できます。そして、Reactのあらゆるstateの更新と同じく、すぐには適用されないので、次の値が前の値に依存するときは更新関数の形を使います。
「+3 with objects」は1しか増えません。3つの呼び出しすべてが同じ this.state.likes を読むからです。「+3 with updaters」は3増えます。「Move to Paris」は name と likes を保ちます(マージ)が、マージは1階層だけなので zip は失われます。残すには address: { ...this.state.address, city: 'Paris' } と書いてください。setState の省略可能な第2引数は更新の後に実行され、コンソールにはそこで新しい住所が表示されます。
イベントハンドラのbind
onClick={this.handleClick} として渡したメソッドは、後でそのオブジェクトなしの普通の関数として呼ばれるので、その中の this は undefined になります。
1つ目のボタンは、undefined の setState を読むことについての TypeError を記録します。2つ目は動きます。クラスフィールドのアロー関数は、インスタンスが作られたときに this を捕まえるからです。古いコードで今もよく見る古い直し方は、コンストラクタでbindすることです。
constructor(props) {
super(props);
this.state = { on: false };
this.handleClick = this.handleClick.bind(this);
}
インラインのアロー関数 onClick={() => this.handleClick()} でも動きます。
ライフサイクルメソッド
クラスは、決まった名前のメソッドでマウント、更新、アンマウントに反応します。よく見るものは次のとおりです。
componentDidMount():最初のレンダリングがDOMに反映された後。タイマー、購読、リクエストを始めます。componentDidUpdate(prevProps, prevState):毎回の更新の後。作業をする前に前回の値と比べてください。そうしないとループします。componentWillUnmount():取り除かれる前。componentDidMountで始めたものを元に戻します。shouldComponentUpdate(nextProps, nextState):falseを返すとレンダリングを飛ばします。
全一覧、実行される順番、そしてすべてをログに出すコンポーネントは、コンポーネントのライフサイクルのページにあります。
クラスを関数コンポーネントに変換する
次のクラスは、よくある3つのライフサイクルメソッドをすべて使っています。毎秒カウントし、label propが変わるとログを出し、取り除かれるとタイマーを止めます。
そして同じコンポーネントを関数にしたものです。各ライフサイクルメソッドはエフェクトの一部になり、関連するセットアップとクリーンアップが2つのメソッドに分かれず、隣り合って並びます。
どちらのタイマーも同じようにカウントし、ラベルが変わるたびにログを出します。エフェクトは最初のレンダリングの後にも実行されるので、関数版はマウント時にも1回ログを出します。どんな変換でも手順は次のとおりです。
this.props.xは分割代入したprop{ x }になります。this.stateの各キーはそれぞれのuseStateになります。セッターはマージせずに置き換えることを忘れないでください。- メソッドはコンポーネントの中の普通の関数になります。bindは不要です。
componentDidMount、componentDidUpdate、componentWillUnmountはuseEffectの呼び出しになり、いつ実行されるかではなく、何をするかでまとめます。createRefはuseRefになり、this.contextと組み合わせたstatic contextTypeはuseContextかuseになります。
エラーバウンダリはクラスのままにしてください。関数の同等品はありません。
PureComponent
普通のクラスコンポーネントは、親がレンダリングされるたびにレンダリングされます。PureComponent はまず新しいpropsとstateを前回のものと1階層だけ比べ、何も変わっていなければレンダリングを飛ばします。React.memoのクラス版です。
ボタンをクリックしてください。PureLabel は同じ text を見るので、記録されるのは PlainLabel rendered だけです。両方に text={'hello ' + clicks} を渡すと、どちらもクリックのたびにレンダリングされます。
比較は浅いので、親のレンダリングのたびに新しいオブジェクトや関数になるprop(style={{ color: 'red' }}、onClick={() => ...})はこれを打ち消します。React.memo と同じ落とし穴です。
React 19がクラスから削除したもの
古いクラスのコードを保守しているなら、React 19ではもう動かないパターンがいくつかあります。
- 文字列ref(
ref="input"とthis.refs.input)。createRef()かrefコールバックを使ってください。 - レガシーコンテキスト(
contextTypes、childContextTypes、getChildContext)。static contextTypeと組み合わせたcreateContextを使ってください。 propTypesは無視されます。TypeScriptか自前のチェックを使ってください。findDOMNodeはなくなりました。代わりにDOM要素にrefを付けてください。
古い componentWillMount、componentWillReceiveProps、componentWillUpdate メソッドもレガシーです。古い名前と UNSAFE_ 付きの名前(UNSAFE_componentWillMount など)のどちらでも今は実行されますが、非推奨でStrictModeが警告するので、そのコードを componentDidMount、componentDidUpdate、getDerivedStateFromProps に移してください。
よくある質問
Reactのクラスコンポーネントは非推奨ですか?
いいえ。React 19でも引き続きサポートされ、今後も動き続けますが、Reactのドキュメントは新しいコードにはフックを使った関数コンポーネントを勧めています。エラーバウンダリには今もクラスが必要です。
クラスコンポーネントと関数コンポーネントの違いは何ですか?
クラスコンポーネントはstateを this.state に持ち、this.setState で更新し、名前の付いたメソッドでライフサイクルに反応します。関数コンポーネントは同じ仕事にフック(useState、useEffect)を使い、this が不要で、より短く書けます。
setStateはstateをすぐに更新しますか?
いいえ。this.setState は更新を予約し、Reactは後でレンダリングするので、呼び出しの直後の this.state はまだ古い値です。新しい値が古い値に依存するなら関数 this.setState((state) => ...) を渡すか、第2引数のコールバックや componentDidUpdate で結果を読んでください。
イベントハンドラの中でthisがundefinedになるのはなぜですか?
onClick={this.handleClick} として渡したクラスメソッドは、そのオブジェクトなしで呼ばれるので、this が undefined になるからです。ハンドラをクラスフィールドのアロー関数(handleClick = () => { ... })として定義するか、コンストラクタでbindしてください。
PureComponentとは何ですか?
propsとstateが前回と浅い比較で等しいときにレンダリングを飛ばす Component です。React.memo のクラス版です。