Menu

Reactのクラスコンポーネント:state、ライフサイクル、関数への変換

クラスコンポーネントは、React.Componentを継承し、renderメソッドからJSXを返すJavaScriptのクラスです。this.props、this.stateとsetState、イベントハンドラのbind、ライフサイクルメソッド、PureComponent、そしてクラスを関数コンポーネントに変換する方法を学びます。

このページのコードはエディタで実行できます - 編集してすぐに結果を確認できます。

クラスコンポーネントは、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回ログを出します。どんな変換でも手順は次のとおりです。

  1. this.props.x は分割代入したprop { x } になります。
  2. this.state の各キーはそれぞれの useState になります。セッターはマージせずに置き換えることを忘れないでください。
  3. メソッドはコンポーネントの中の普通の関数になります。bindは不要です。
  4. componentDidMount、componentDidUpdate、componentWillUnmount は useEffect の呼び出しになり、いつ実行されるかではなく、何をするかでまとめます。
  5. 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 のクラス版です。

Coddyのプログラミング言語のイラスト

Coddyでコードを学ぼう

始める