落とし穴

コンポーネントはクラスではなく関数として定義することを推奨します。移行方法をご覧ください。

Component は、JavaScript クラスとして定義される React コンポーネントの基本クラスです。クラスコンポーネントは React によって引き続きサポートされますが、新しいコードでは使用しないことを推奨します。

class Greeting extends Component {
render() {
return <h1>Hello, {this.props.name}!</h1>;
}
}

リファレンス

Component

React コンポーネントをクラスとして定義するには、組み込みのComponent クラスを拡張し、render メソッドを定義します。

import { Component } from 'react';

class Greeting extends Component {
render() {
return <h1>Hello, {this.props.name}!</h1>;
}
}

render メソッドのみが必須で、その他のメソッドはオプションです。

以下の例を参照してください。


context

クラスコンポーネントのコンテキストは、this.context として利用できます。これは、static contextType を使用して、受け取りたいコンテキストを指定した場合にのみ利用できます。

クラスコンポーネントが一度に読み取れるコンテキストは 1 つだけです。

class Button extends Component {
static contextType = ThemeContext;

render() {
const theme = this.context;
const className = 'button-' + theme;
return (
<button className={className}>
{this.props.children}
</button>
);
}
}

注意

クラスコンポーネントで this.context を読み取ることは、関数コンポーネントの useContext と同等です。

移行方法をご覧ください。


props

クラスコンポーネントに渡される props は、this.props として利用できます。

class Greeting extends Component {
render() {
return <h1>Hello, {this.props.name}!</h1>;
}
}

<Greeting name="Taylor" />

注意

クラスコンポーネントでthis.propsを読むことは、関数コンポーネントでpropsを宣言することと同じです。

移行方法をご覧ください。


state

クラスコンポーネントのstateは、this.stateとして利用可能です。stateフィールドはオブジェクトでなければなりません。stateを直接変更しないでください。stateを変更したい場合は、setStateを新しいstateとともに呼び出してください。

class Counter extends Component {
state = {
age: 42,
};

handleAgeChange = () => {
this.setState({
age: this.state.age + 1
});
};

render() {
return (
<>
<button onClick={this.handleAgeChange}>
Increment age
</button>
<p>You are {this.state.age}.</p>
</>
);
}
}

注意

クラスコンポーネントでstateを定義することは、関数コンポーネントでuseStateを呼び出すことと同じです。

移行方法をご覧ください。


constructor(props)

constructorは、クラスコンポーネントがマウント(画面に追加される)される前に実行されます。通常、コンストラクターはReactで2つの目的のみで使用されます。それは、stateを宣言することと、クラスメソッドをクラスインスタンスにバインドすることです。

class Counter extends Component {
constructor(props) {
super(props);
this.state = { counter: 0 };
this.handleClick = this.handleClick.bind(this);
}

handleClick() {
// ...
}

最新のJavaScript構文を使用する場合、コンストラクターはほとんど必要ありません。代わりに、上記のコードを、モダンブラウザとBabelのようなツールでサポートされているパブリッククラスフィールド構文を使用して書き換えることができます。

class Counter extends Component {
state = { counter: 0 };

handleClick = () => {
// ...
}

コンストラクターには、副作用やサブスクリプションを含めるべきではありません。

パラメーター

  • props: コンポーネントの初期props。

戻り値

constructorは何も返す必要はありません。

注意点

  • コンストラクターで副作用やサブスクリプションを実行しないでください。代わりに、componentDidMountを使用してください。

  • コンストラクター内では、他のステートメントの前にsuper(props)を呼び出す必要があります。そうしないと、コンストラクターの実行中にthis.propsundefinedになり、混乱を招きバグの原因となる可能性があります。

  • コンストラクターは、this.stateを直接代入できる唯一の場所です。他のすべてのメソッドでは、代わりにthis.setState()を使用する必要があります。コンストラクターでsetStateを呼び出さないでください。

  • サーバーレンダリングを使用する場合、コンストラクターはサーバー上でも実行され、その後にrenderメソッドが続きます。ただし、componentDidMountcomponentWillUnmountのようなライフサイクルメソッドはサーバー上では実行されません。

  • StrictModeがオンの場合、Reactは開発時にconstructorを2回呼び出し、そのうちの1つのインスタンスを破棄します。これは、constructorの外に移動する必要がある偶発的な副作用に気づくのに役立ちます。

注意

関数コンポーネントには、constructor に完全に相当するものはありません。関数コンポーネントでstateを宣言するには、useState を呼び出します。初期stateの再計算を避けるには、useStateに関数を渡してください。


componentDidCatch(error, info)

componentDidCatchを定義すると、Reactは、子コンポーネント(遠い子コンポーネントを含む)がレンダリング中にエラーをスローしたときにそれを呼び出します。これにより、プロダクション環境でエラーをエラー報告サービスにログを記録できます。

通常、エラーに応じてstateを更新し、エラーメッセージをユーザーに表示できるようにするstatic getDerivedStateFromErrorと組み合わせて使用します。これらのメソッドを持つコンポーネントは、エラー境界と呼ばれます。

例をご覧ください。

パラメータ

  • error: スローされたエラー。実際には通常、Errorのインスタンスになりますが、JavaScriptでは文字列やnullを含む任意の値をthrowできるため、保証されていません。

  • info: エラーに関する追加情報を含むオブジェクト。そのcomponentStackフィールドには、スローしたコンポーネントと、その親コンポーネントすべての名前とソースの場所を含むスタックトレースが含まれています。本番環境では、コンポーネント名がminifyされます。本番環境でのエラー報告を設定している場合は、通常のJavaScriptエラーのスタックと同様に、ソースマップを使用してコンポーネントスタックをデコードできます。

戻り値

componentDidCatchは何も返すべきではありません。

注意点

  • 以前は、UIを更新してフォールバックエラーメッセージを表示するために、componentDidCatch内でsetStateを呼び出すのが一般的でした。これは、static getDerivedStateFromErrorを定義することが推奨されています。

  • Reactのプロダクションビルドと開発ビルドでは、componentDidCatchがエラーを処理する方法がわずかに異なります。開発環境では、エラーはwindowまでバブルアップします。つまり、window.onerrorまたはwindow.addEventListener('error', callback)は、componentDidCatchによってキャッチされたエラーをインターセプトします。一方、本番環境では、エラーはバブルアップしません。つまり、祖先エラーハンドラーは、componentDidCatchによって明示的にキャッチされなかったエラーのみを受け取ります。

注意

関数コンポーネントには、componentDidCatchに直接対応するものはありません。クラスコンポーネントの作成を避けたい場合は、上記のErrorBoundaryコンポーネントを1つ作成し、アプリ全体で使用してください。あるいは、代わりに、それを行うreact-error-boundaryパッケージを使用できます。


componentDidMount()

componentDidMountメソッドを定義すると、Reactはコンポーネントが画面に追加(マウント)されたときにそれを呼び出します。これは、データフェッチを開始したり、サブスクリプションを設定したり、DOMノードを操作したりする一般的な場所です。

componentDidMountを実装する場合は、バグを回避するために、通常、他のライフサイクルメソッドを実装する必要があります。たとえば、componentDidMountが一部のstateまたはpropsを読み取る場合は、変更を処理するためにcomponentDidUpdateも実装し、componentDidMountが行っていたことをクリーンアップするためにcomponentWillUnmountも実装する必要があります。

class ChatRoom extends Component {
state = {
serverUrl: 'https://localhost:1234'
};

componentDidMount() {
this.setupConnection();
}

componentDidUpdate(prevProps, prevState) {
if (
this.props.roomId !== prevProps.roomId ||
this.state.serverUrl !== prevState.serverUrl
) {
this.destroyConnection();
this.setupConnection();
}
}

componentWillUnmount() {
this.destroyConnection();
}

// ...
}

その他の例をご覧ください。

パラメータ

componentDidMount はパラメータを受け取りません。

戻り値

componentDidMount は何も返すべきではありません。

注意点

  • StrictMode が有効な開発環境では、React は componentDidMount を呼び出した後、すぐに componentWillUnmount を呼び出し、再度 componentDidMount を呼び出します。これは、componentWillUnmount の実装を忘れていないか、そのロジックが componentDidMount の動作を完全に「反映」していない場合に気づくのに役立ちます。

  • componentDidMount 内ですぐに setState を呼び出しても構いませんが、可能な限り避けるのが最善です。これにより追加のレンダリングが発生しますが、ブラウザが画面を更新する前に発生します。これにより、この場合 render が 2 回呼び出されるとしても、ユーザーは中間の状態を見ることがないことが保証されます。このパターンはパフォーマンスの問題を引き起こすことが多いため、注意して使用してください。ほとんどの場合、代わりに constructor で初期状態を割り当てるべきです。ただし、モーダルやツールチップのように、サイズや位置に依存するものをレンダリングする前に DOM ノードを測定する必要がある場合は、必要になることがあります。

注意

多くのユースケースでは、クラスコンポーネントで componentDidMount, componentDidUpdate, および componentWillUnmount をまとめて定義することは、関数コンポーネントで useEffect を呼び出すことと同等です。コードをブラウザの描画前に実行することが重要な稀なケースでは、useLayoutEffect の方がより近い対応となります。

移行方法をご覧ください。


componentDidUpdate(prevProps, prevState, snapshot?)

componentDidUpdate メソッドを定義した場合、React は、コンポーネントが更新された props または state で再レンダリングされた直後にそれを呼び出します。このメソッドは、初期レンダリングでは呼び出されません。

更新後に DOM を操作するために使用できます。また、現在の props と前の props を比較する限り (たとえば、props が変更されていない場合はネットワークリクエストが必要ない場合があります)、ネットワークリクエストを実行する一般的な場所でもあります。通常は、componentDidMount および componentWillUnmount と組み合わせて使用します:

class ChatRoom extends Component {
state = {
serverUrl: 'https://localhost:1234'
};

componentDidMount() {
this.setupConnection();
}

componentDidUpdate(prevProps, prevState) {
if (
this.props.roomId !== prevProps.roomId ||
this.state.serverUrl !== prevState.serverUrl
) {
this.destroyConnection();
this.setupConnection();
}
}

componentWillUnmount() {
this.destroyConnection();
}

// ...
}

その他の例をご覧ください。

パラメータ

  • prevProps: 更新前の props。 prevPropsthis.props と比較して、何が変更されたかを確認します。

  • prevState: 更新前の state。 prevStatethis.state と比較して、何が変更されたかを確認します。

  • snapshot: getSnapshotBeforeUpdate を実装した場合、snapshot にはそのメソッドから返された値が含まれます。それ以外の場合は、undefined になります。

戻り値

componentDidUpdate は何も返すべきではありません。

注意点

  • shouldComponentUpdate が定義されていて false を返す場合、componentDidUpdate は呼び出されません。

  • componentDidUpdate 内のロジックは通常、this.propsprevProps、そして this.stateprevState を比較する条件でラップする必要があります。そうしないと、無限ループを作成するリスクがあります。

  • componentDidUpdate 内で setState をすぐに呼び出すこともできますが、可能な場合は避けるのが最善です。これにより余分なレンダリングが発生しますが、ブラウザが画面を更新する前に発生します。これにより、この場合 render が2回呼び出されるとしても、ユーザーは中間状態を見ないことが保証されます。このパターンはしばしばパフォーマンスの問題を引き起こしますが、サイズや位置に依存する何かをレンダリングする前にDOMノードを測定する必要がある場合、モーダルやツールチップなどのまれなケースで必要になることがあります。

注意

多くのユースケースでは、クラスコンポーネントで componentDidMount, componentDidUpdate, および componentWillUnmount をまとめて定義することは、関数コンポーネントで useEffect を呼び出すことと同等です。コードをブラウザの描画前に実行することが重要な稀なケースでは、useLayoutEffect の方がより近い対応となります。

移行方法をご覧ください。


componentWillMount()

非推奨

このAPIは、componentWillMount から UNSAFE_componentWillMount に名前が変更されました。古い名前は非推奨になりました。今後のReactのメジャーバージョンでは、新しい名前のみが機能します。

コンポーネントを自動的に更新するには、rename-unsafe-lifecycles codemod を実行してください。


componentWillReceiveProps(nextProps)

非推奨

このAPIは、componentWillReceiveProps から UNSAFE_componentWillReceiveProps に名前が変更されました。古い名前は非推奨になりました。今後のReactのメジャーバージョンでは、新しい名前のみが機能します。

コンポーネントを自動的に更新するには、rename-unsafe-lifecycles codemod を実行してください。


componentWillUpdate(nextProps, nextState)

非推奨

このAPIは、componentWillUpdate から UNSAFE_componentWillUpdate に名前が変更されました。古い名前は非推奨になりました。今後のReactのメジャーバージョンでは、新しい名前のみが機能します。

コンポーネントを自動的に更新するには、rename-unsafe-lifecycles codemod を実行してください。


componentWillUnmount()

componentWillUnmount メソッドを定義すると、Reactは、コンポーネントが画面から削除される(アンマウントされる)前にそれを呼び出します。これは、データフェッチをキャンセルしたり、サブスクリプションを削除したりする一般的な場所です。

componentWillUnmount 内のロジックは、componentDidMount 内のロジックを「ミラーリング」する必要があります。たとえば、componentDidMount がサブスクリプションを設定する場合、componentWillUnmount はそのサブスクリプションをクリーンアップする必要があります。componentWillUnmount のクリーンアップロジックが一部のpropsまたはstateを読み取る場合、通常は componentDidUpdate を実装して、古いpropsおよびstateに対応するリソース(サブスクリプションなど)をクリーンアップする必要もあります。

class ChatRoom extends Component {
state = {
serverUrl: 'https://localhost:1234'
};

componentDidMount() {
this.setupConnection();
}

componentDidUpdate(prevProps, prevState) {
if (
this.props.roomId !== prevProps.roomId ||
this.state.serverUrl !== prevState.serverUrl
) {
this.destroyConnection();
this.setupConnection();
}
}

componentWillUnmount() {
this.destroyConnection();
}

// ...
}

その他の例をご覧ください。

パラメーター

componentWillUnmount はパラメーターを受け取りません。

戻り値

componentWillUnmount は何も返すべきではありません。

注意点

  • Strict Mode がオンの場合、開発環境では React は componentDidMount を呼び出した後、直ちに componentWillUnmount を呼び出し、その後再び componentDidMount を呼び出します。これにより、componentWillUnmount の実装を忘れていないか、またはそのロジックが componentDidMount の処理を完全に「ミラーリング」していない場合に気づくことができます。

注意

多くのユースケースでは、クラスコンポーネントで componentDidMount, componentDidUpdate, および componentWillUnmount をまとめて定義することは、関数コンポーネントで useEffect を呼び出すことと同等です。コードをブラウザの描画前に実行することが重要な稀なケースでは、useLayoutEffect の方がより近い対応となります。

移行方法をご覧ください。


forceUpdate(callback?)

コンポーネントを強制的に再レンダリングします。

通常、これは必要ありません。コンポーネントの render メソッドが this.propsthis.state、または this.context からのみ読み取る場合、コンポーネント内または親コンポーネントのいずれかで setState を呼び出すと、自動的に再レンダリングされます。しかし、コンポーネントの render メソッドが外部データソースから直接読み取る場合、そのデータソースが変更されたときに React にユーザーインターフェースを更新するように指示する必要があります。それが forceUpdate でできることです。

forceUpdate の使用はすべて避け、render 内では this.propsthis.state からのみ読み取るようにしてください。

パラメーター

  • 任意 callback 指定された場合、React は更新がコミットされた後に、指定した callback を呼び出します。

戻り値

forceUpdate は何も返しません。

注意点

  • forceUpdate を呼び出すと、React は shouldComponentUpdate を呼び出さずに再レンダリングします。

注意

外部データソースを読み取り、forceUpdate を使用して、その変更に応じてクラスコンポーネントを再レンダリングする方法は、関数コンポーネントでの useSyncExternalStore に取って代わられました。


getSnapshotBeforeUpdate(prevProps, prevState)

getSnapshotBeforeUpdate を実装すると、React は DOM を更新する直前にそれを呼び出します。これにより、コンポーネントは、変更される可能性のある DOM から(スクロール位置などの)情報をキャプチャできます。このライフサイクルメソッドによって返された値は、componentDidUpdate のパラメーターとして渡されます。

例えば、アップデート中にスクロール位置を維持する必要があるチャットスレッドのような UI で使用できます。

class ScrollingList extends React.Component {
constructor(props) {
super(props);
this.listRef = React.createRef();
}

getSnapshotBeforeUpdate(prevProps, prevState) {
// Are we adding new items to the list?
// Capture the scroll position so we can adjust scroll later.
if (prevProps.list.length < this.props.list.length) {
const list = this.listRef.current;
return list.scrollHeight - list.scrollTop;
}
return null;
}

componentDidUpdate(prevProps, prevState, snapshot) {
// If we have a snapshot value, we've just added new items.
// Adjust scroll so these new items don't push the old ones out of view.
// (snapshot here is the value returned from getSnapshotBeforeUpdate)
if (snapshot !== null) {
const list = this.listRef.current;
list.scrollTop = list.scrollHeight - snapshot;
}
}

render() {
return (
<div ref={this.listRef}>{/* ...contents... */}</div>
);
}
}

上記の例では、scrollHeight プロパティを getSnapshotBeforeUpdate 内で直接読み取ることが重要です。renderUNSAFE_componentWillReceiveProps、または UNSAFE_componentWillUpdate で読み取るのは安全ではありません。これらのメソッドが呼び出されてから React が DOM を更新するまでの間に時間差が生じる可能性があるからです。

パラメータ

  • prevProps: 更新前の props。 prevPropsthis.props と比較して、何が変更されたかを確認します。

  • prevState: 更新前の state。 prevStatethis.state と比較して、何が変更されたかを確認します。

戻り値

任意の型のスナップショット値を返すか、null を返す必要があります。返された値は、componentDidUpdate の第3引数として渡されます。

注意点

  • shouldComponentUpdate が定義されていて、false を返す場合、getSnapshotBeforeUpdate は呼び出されません。

注意

現時点では、関数コンポーネントに getSnapshotBeforeUpdate に相当するものはありません。このユースケースは非常にまれですが、必要な場合は、今のところクラスコンポーネントを記述する必要があります。


render()

render メソッドは、クラスコンポーネントで唯一必須のメソッドです。

render メソッドは、画面に表示したいものを指定する必要があります。例えば

import { Component } from 'react';

class Greeting extends Component {
render() {
return <h1>Hello, {this.props.name}!</h1>;
}
}

React は任意のタイミングで render を呼び出す可能性があるため、特定の時間に実行されると想定すべきではありません。通常、render メソッドは JSX の一部を返す必要がありますが、いくつかの 他の戻り値の型 (文字列など) もサポートされています。返される JSX を計算するために、render メソッドは this.propsthis.state、および this.context を読み取ることができます。

render メソッドは純粋関数として記述する必要があります。つまり、props、state、および context が同じであれば、同じ結果を返す必要があります。また、(サブスクリプションの設定などの) 副作用を含めたり、ブラウザ API と対話したりしないでください。副作用は、イベントハンドラーまたは componentDidMount などのメソッドで発生する必要があります。

パラメータ

render はパラメータを受け取りません。

戻り値

render は、有効なReactノードであればどのようなものでも返すことができます。これには、<div />のようなReact要素、文字列、数値、ポータル、空のノード(nullundefinedtrue、およびfalse)、そしてReactノードの配列が含まれます。

注意事項

  • renderは、props、state、およびcontextの純粋関数として記述する必要があります。副作用があってはなりません。

  • shouldComponentUpdateが定義されており、falseを返す場合、renderは呼び出されません。

  • Strict Modeがオンになっている場合、Reactは開発時にrenderを2回呼び出し、そのうち1つの結果を破棄します。これは、renderメソッドから移動する必要がある、意図しない副作用に気付くのに役立ちます。

  • renderの呼び出しと、それに続くcomponentDidMountまたはcomponentDidUpdateの呼び出しとの間に、一対一の対応関係はありません。一部のrender呼び出しの結果は、Reactが有益だと判断した場合に破棄されることがあります。


setState(nextState, callback?)

Reactコンポーネントの状態を更新するには、setStateを呼び出します。

class Form extends Component {
state = {
name: 'Taylor',
};

handleNameChange = (e) => {
const newName = e.target.value;
this.setState({
name: newName
});
}

render() {
return (
<>
<input value={this.state.name} onChange={this.handleNameChange} />
<p>Hello, {this.state.name}.</p>
</>
);
}
}

setStateは、コンポーネントの状態への変更をキューに入れます。これにより、このコンポーネントとその子要素が新しい状態で再レンダリングする必要があることをReactに伝えます。これは、インタラクションに応答してユーザーインターフェイスを更新する主な方法です。

落とし穴

setStateを呼び出すことは、実行中のコードで現在の状態を変更するものではありません

function handleClick() {
console.log(this.state.name); // "Taylor"
this.setState({
name: 'Robin'
});
console.log(this.state.name); // Still "Taylor"!
}

これは、次のレンダリングから開始して、this.stateが返すものにのみ影響します。

setStateに関数を渡すこともできます。これにより、前の状態に基づいて状態を更新できます

handleIncreaseAge = () => {
this.setState(prevState => {
return {
age: prevState.age + 1
};
});
}

これを行う必要はありませんが、同じイベント中に状態を複数回更新したい場合に便利です。

パラメーター

  • nextState:オブジェクトまたは関数のいずれか。

    • nextStateとしてオブジェクトを渡すと、this.stateに浅くマージされます。
    • nextStateとして関数を渡すと、更新関数として扱われます。これは純粋である必要があり、保留中の状態とpropsを引数として受け取り、this.stateに浅くマージされるオブジェクトを返す必要があります。Reactは、更新関数をキューに入れ、コンポーネントを再レンダリングします。次のレンダリング中に、Reactは、キューに入れられたすべてのアップデーターを前の状態に適用して次の状態を計算します。
  • オプション callback:指定した場合、Reactは更新がコミットされた後に指定したcallbackを呼び出します。

戻り値

setStateは何も返しません。

注意事項

  • setStateは、コンポーネントを更新するための即時コマンドではなく、リクエストと考えてください。複数のコンポーネントがイベントに応答して状態を更新する場合、Reactは更新をバッチ処理し、イベントの最後に1回のパスでまとめて再レンダリングします。特定の状態更新を同期的に適用する必要があるまれなケースでは、flushSyncでラップできますが、パフォーマンスが低下する可能性があります。

  • setStatethis.stateをすぐに更新しません。これにより、setStateを呼び出した直後にthis.stateを読むと、潜在的な落とし穴になります。代わりに、componentDidUpdateまたはsetStateのcallback引数を使用してください。どちらも、更新が適用された後に発火することが保証されています。前の状態に基づいて状態を設定する必要がある場合は、上記のように、関数をnextStateに渡すことができます。

注意

クラスコンポーネントでのsetStateの呼び出しは、関数コンポーネントでのset関数の呼び出しと似ています。

移行方法をご覧ください。


shouldComponentUpdate(nextProps, nextState, nextContext)

shouldComponentUpdate を定義した場合、React はそれを呼び出して再レンダリングをスキップできるかどうかを判断します。

手動で記述することに自信がある場合は、this.propsnextProps を比較し、this.statenextState を比較して、false を返して、React に更新をスキップできることを伝えることができます。

class Rectangle extends Component {
state = {
isHovered: false
};

shouldComponentUpdate(nextProps, nextState) {
if (
nextProps.position.x === this.props.position.x &&
nextProps.position.y === this.props.position.y &&
nextProps.size.width === this.props.size.width &&
nextProps.size.height === this.props.size.height &&
nextState.isHovered === this.state.isHovered
) {
// Nothing has changed, so a re-render is unnecessary
return false;
}
return true;
}

// ...
}

React は、新しい props または state が受信されたときにレンダリングする前に shouldComponentUpdate を呼び出します。デフォルトは true です。このメソッドは、最初のレンダリング時や、forceUpdate が使用された場合には呼び出されません。

パラメータ

  • nextProps:コンポーネントがこれからレンダリングする props です。nextPropsthis.props と比較して、何が変更されたかを判断します。
  • nextState:コンポーネントがこれからレンダリングする state です。nextStatethis.state と比較して、何が変更されたかを判断します。
  • nextContext:コンポーネントがこれからレンダリングする context です。nextContextthis.context と比較して、何が変更されたかを判断します。static contextType を指定した場合のみ使用可能です。

戻り値

コンポーネントを再レンダリングする場合は true を返します。これがデフォルトの動作です。

再レンダリングをスキップできることを React に伝えるには、false を返します。

注意点

  • このメソッドは、あくまでパフォーマンス最適化のために存在します。これがなくてもコンポーネントが壊れる場合は、まずそれを修正してください。

  • shouldComponentUpdate を手動で記述する代わりに、PureComponent を使用することを検討してください。PureComponent は props と state を浅く比較し、必要な更新をスキップする可能性を減らします。

  • shouldComponentUpdate で深い等価性チェックを行ったり、JSON.stringify を使用したりすることはお勧めしません。パフォーマンスが予測不可能になり、すべての props と state のデータ構造に依存するようになります。最良の場合でも、アプリケーションに数秒間の停止をもたらすリスクがあり、最悪の場合にはクラッシュする危険性があります。

  • false を返しても、子コンポーネントが *自身の* state の変更時に再レンダリングされるのを防ぐことはできません。

  • false を返しても、コンポーネントが再レンダリングされないことが *保証* されるわけではありません。React は戻り値をヒントとして使用しますが、他の理由でレンダリングすることが適切な場合は、コンポーネントを再レンダリングすることを選択する場合があります。

注意

shouldComponentUpdate でクラスコンポーネントを最適化することは、memo で関数コンポーネントを最適化することと似ています。関数コンポーネントでは、useMemo を使用して、よりきめ細かい最適化も可能です。


UNSAFE_componentWillMount()

UNSAFE_componentWillMount を定義すると、React は constructor の直後にそれを呼び出します。これは歴史的な理由でのみ存在しており、新しいコードでは使用すべきではありません。代わりに、以下のいずれかの代替手段を使用してください。

  • state を初期化するには、クラスフィールドとして state を宣言するか、constructor 内で this.state を設定します。
  • 副作用を実行したり、サブスクリプションを設定する必要がある場合は、そのロジックをcomponentDidMountに移動してください。

安全でないライフサイクルから移行する例を参照してください。

パラメータ

UNSAFE_componentWillMountは、パラメータを受け取りません。

戻り値

UNSAFE_componentWillMountは、何も返すべきではありません。

注意事項

  • コンポーネントがstatic getDerivedStateFromPropsまたはgetSnapshotBeforeUpdateを実装している場合、UNSAFE_componentWillMountは呼び出されません。

  • 名前が示すにもかかわらず、UNSAFE_componentWillMountは、アプリがSuspenseのような最新のReact機能を使用している場合、コンポーネントがマウントされることを保証しません。レンダリングの試行が中断された場合(たとえば、一部の子コンポーネントのコードがまだロードされていない場合)、Reactは進行中のツリーを破棄し、次の試行時にコンポーネントを最初から構築しようとします。これがこのメソッドが「安全でない」理由です。(サブスクリプションの追加など)マウントに依存するコードは、componentDidMountに記述する必要があります。

  • UNSAFE_componentWillMountは、サーバーレンダリング中に実行される唯一のライフサイクルメソッドです。実際上、constructorと同一であるため、この種のロジックには代わりにconstructorを使用する必要があります。

注意

クラスコンポーネントでUNSAFE_componentWillMount内でsetStateを呼び出して状態を初期化することは、関数コンポーネントでuseStateに初期状態としてその状態を渡すことと同等です。


UNSAFE_componentWillReceiveProps(nextProps, nextContext)

UNSAFE_componentWillReceivePropsを定義すると、コンポーネントが新しいpropsを受け取るときにReactによって呼び出されます。これは歴史的な理由でのみ存在し、新しいコードでは使用しないでください。代わりに、代替手段のいずれかを使用してください。

  • propsの変更に応じて副作用を実行する必要がある場合(たとえば、データをフェッチしたり、アニメーションを実行したり、サブスクリプションを再初期化したりする場合)、そのロジックを代わりにcomponentDidUpdateに移動してください。
  • propsが変更された場合にのみ一部のデータの再計算を避ける必要がある場合は、代わりにメモ化ヘルパーを使用してください。
  • propsが変更されたときに一部の状態を「リセット」する必要がある場合は、コンポーネントを完全に制御されたコンポーネントにするか、またはキー付きで完全に制御されないコンポーネントにすることを検討してください。
  • propsが変更されたときに一部の状態を「調整」する必要がある場合は、レンダリング中にpropsのみから必要なすべての情報を計算できるかどうかを確認してください。できない場合は、代わりにstatic getDerivedStateFromPropsを使用してください。

安全でないライフサイクルから移行する例を参照してください。

パラメータ

  • nextProps: コンポーネントが親コンポーネントから受け取ろうとしている次の props です。 this.propsnextProps を比較して、何が変更されたかを判断します。
  • nextContext: コンポーネントが最も近いプロバイダーから受け取ろうとしている次のコンテキストです。 this.contextnextContext を比較して、何が変更されたかを判断します。 static contextType を指定した場合にのみ利用可能です。

戻り値

UNSAFE_componentWillReceiveProps は何も返す必要はありません。

注意点

  • コンポーネントが static getDerivedStateFromProps または getSnapshotBeforeUpdate を実装している場合、UNSAFE_componentWillReceiveProps は呼び出されません。

  • その名前にもかかわらず、UNSAFE_componentWillReceiveProps は、アプリが Suspense のような最新の React 機能を使用している場合、コンポーネントがそれらの props を受け取ることを保証するものではありません。レンダリングの試行が中断された場合(たとえば、一部の子コンポーネントのコードがまだロードされていない場合)、React は進行中のツリーを破棄し、次の試行時にコンポーネントを最初から作成しようとします。次のレンダリング試行時までに、props が異なる可能性があります。これが、このメソッドが「安全でない」理由です。(サブスクリプションのリセットなど)コミットされた更新に対してのみ実行する必要があるコードは、代わりに componentDidUpdate に移動する必要があります。

  • UNSAFE_componentWillReceiveProps は、コンポーネントが前回と異なる props を受け取ったことを意味するものではありません。変更があったかどうかを確認するには、自分で nextPropsthis.props を比較する必要があります。

  • React は、マウント時の初期 props で UNSAFE_componentWillReceiveProps を呼び出しません。コンポーネントの props の一部が更新される場合にのみ、このメソッドを呼び出します。たとえば、setState を呼び出しても、通常は同じコンポーネント内で UNSAFE_componentWillReceiveProps はトリガーされません。

注意

クラスコンポーネントの UNSAFE_componentWillReceiveProps 内で setState を呼び出して状態を「調整」することは、関数コンポーネントで useState から set 関数をレンダリング中に呼び出すことに相当します。


UNSAFE_componentWillUpdate(nextProps, nextState)

UNSAFE_componentWillUpdate を定義すると、React は新しい props または state でレンダリングする前にそれを呼び出します。これは歴史的な理由でのみ存在し、新しいコードで使用すべきではありません。代わりに、代替案のいずれかを使用してください。

  • props または state の変更に応じて副作用(たとえば、データのフェッチ、アニメーションの実行、またはサブスクリプションの再初期化)を実行する必要がある場合は、代わりにそのロジックを componentDidUpdate に移動します。
  • componentDidUpdate で後で使用できるように、DOM から何らかの情報を(たとえば、現在のスクロール位置を保存するために)読み取る必要がある場合は、代わりに getSnapshotBeforeUpdate 内で読み取ります。

安全でないライフサイクルから移行する例を参照してください。

パラメータ

  • nextProps:コンポーネントがこれからレンダリングする props です。nextPropsthis.props と比較して、何が変更されたかを判断します。
  • nextState: コンポーネントがレンダリングしようとしている次の state です。 this.statenextState を比較して、何が変更されたかを判断します。

以下を返します:

UNSAFE_componentWillUpdate は何も返すべきではありません。

注意点

  • UNSAFE_componentWillUpdate は、shouldComponentUpdate が定義され、false を返す場合は呼び出されません。

  • コンポーネントが static getDerivedStateFromProps または getSnapshotBeforeUpdate を実装している場合、UNSAFE_componentWillUpdate は呼び出されません。

  • componentWillUpdate の実行中に setState (または Redux アクションのディスパッチなど、setState が呼び出されることになるメソッド) を呼び出すことはサポートされていません。

  • 名前にもかかわらず、UNSAFE_componentWillUpdate は、アプリが Suspense のような最新の React 機能を使用している場合、コンポーネントが更新されることを保証しません。レンダーの試行が中断された場合 (たとえば、一部の子コンポーネントのコードがまだ読み込まれていない場合)、React は進行中のツリーを破棄し、次の試行時にコンポーネントを最初から構築しようとします。次のレンダーの試行までに、props と state が異なる場合があります。これが、このメソッドが「unsafe」である理由です。(サブスクリプションのリセットなど) コミットされた更新に対してのみ実行されるコードは、componentDidUpdate に記述する必要があります。

  • UNSAFE_componentWillUpdate は、コンポーネントが前回とは異なる props または state を受け取ったという意味ではありません。何か変更があったかどうかを確認するには、nextPropsthis.props と比較し、nextStatethis.state と比較する必要があります。

  • React は、マウント中に初期 props と state を使用して UNSAFE_componentWillUpdate を呼び出しません。

注意

関数コンポーネントには、UNSAFE_componentWillUpdate に直接対応するものはありません。


static contextType

クラスコンポーネントから this.context を読み取りたい場合は、読み取る必要があるコンテキストを指定する必要があります。static contextType として指定するコンテキストは、以前に createContext によって作成された値である必要があります。

class Button extends Component {
static contextType = ThemeContext;

render() {
const theme = this.context;
const className = 'button-' + theme;
return (
<button className={className}>
{this.props.children}
</button>
);
}
}

注意

クラスコンポーネントで this.context を読み取ることは、関数コンポーネントの useContext と同等です。

移行方法をご覧ください。


static defaultProps

クラスのデフォルト props を設定するために static defaultProps を定義できます。これらは undefined および欠落している props に使用されますが、null props には使用されません。

たとえば、color プロパティのデフォルトを 'blue' に設定する方法は次のとおりです。

class Button extends Component {
static defaultProps = {
color: 'blue'
};

render() {
return <button className={this.props.color}>click me</button>;
}
}

color prop が提供されていないか、undefined の場合、デフォルトで 'blue' に設定されます

<>
{/* this.props.color is "blue" */}
<Button />

{/* this.props.color is "blue" */}
<Button color={undefined} />

{/* this.props.color is null */}
<Button color={null} />

{/* this.props.color is "red" */}
<Button color="red" />
</>

注意

クラスコンポーネントで defaultProps を定義することは、関数コンポーネントで デフォルト値を使用するのと似ています。


static getDerivedStateFromError(error)

static getDerivedStateFromError を定義すると、React は子コンポーネント(遠い子コンポーネントを含む)がレンダリング中にエラーをスローしたときに、それを呼び出します。これにより、UI をクリアする代わりにエラーメッセージを表示できます。

通常、これは componentDidCatch と一緒に使用され、エラーレポートを何らかの分析サービスに送信できます。これらのメソッドを持つコンポーネントは、エラー境界と呼ばれます。

例をご覧ください。

パラメーター

  • error: スローされたエラー。実際には通常、Errorのインスタンスになりますが、JavaScriptでは文字列やnullを含む任意の値をthrowできるため、保証されていません。

戻り値

static getDerivedStateFromError は、エラーメッセージを表示するようにコンポーネントに指示する状態を返す必要があります。

注意点

  • static getDerivedStateFromError は純粋な関数である必要があります。副作用(たとえば、分析サービスを呼び出すなど)を実行する場合は、componentDidCatch も実装する必要があります。

注意

関数コンポーネントには、static getDerivedStateFromError に直接相当するものはありません。クラスコンポーネントの作成を避けたい場合は、上記のような単一の ErrorBoundary コンポーネントを作成し、アプリケーション全体で使用してください。または、それを実行する react-error-boundary パッケージを使用してください。


static getDerivedStateFromProps(props, state)

static getDerivedStateFromProps を定義すると、React は初期マウント時とそれ以降の更新時の両方で、render を呼び出す直前にそれを呼び出します。状態を更新するオブジェクトを返すか、何も更新しない場合は null を返す必要があります。

このメソッドは、状態が時間の経過に伴うプロップスの変化に依存するまれなユースケースのために存在します。たとえば、この Form コンポーネントは、userID プロップスが変化すると email 状態をリセットします。

class Form extends Component {
state = {
email: this.props.defaultEmail,
prevUserID: this.props.userID
};

static getDerivedStateFromProps(props, state) {
// Any time the current user changes,
// Reset any parts of state that are tied to that user.
// In this simple example, that's just the email.
if (props.userID !== state.prevUserID) {
return {
prevUserID: props.userID,
email: props.defaultEmail
};
}
return null;
}

// ...
}

このパターンでは、状態(prevUserID など)でプロップスの前の値(userID など)を保持する必要があることに注意してください。

落とし穴

派生状態は冗長なコードにつながり、コンポーネントを理解しにくくします。よりシンプルな代替手段をよく理解していることを確認してください。

パラメーター

  • props:コンポーネントが次にレンダリングしようとしているプロップス。
  • state:コンポーネントが次にレンダリングしようとしている状態。

戻り値

static getDerivedStateFromProps は、stateを更新するためのオブジェクトを返すか、何も更新しない場合はnullを返します。

注意点

  • このメソッドは、原因に関わらず、すべてのレンダリング時に発火します。これは、親が再レンダリングを引き起こした場合のみ発火し、ローカルのsetStateの結果としては発火しないUNSAFE_componentWillReceivePropsとは異なります。

  • このメソッドは、コンポーネントインスタンスにアクセスできません。必要であれば、コンポーネントのpropsとstateの純粋関数をクラス定義の外に抽出することで、static getDerivedStateFromPropsと他のクラスメソッドの間でコードを再利用できます。

注意

クラスコンポーネントでstatic getDerivedStateFromPropsを実装することは、関数コンポーネントでレンダリング中にuseStateset関数を呼び出すことと同じです。


使用法

クラスコンポーネントの定義

React コンポーネントをクラスとして定義するには、組み込みのComponent クラスを拡張し、render メソッドを定義します。

import { Component } from 'react';

class Greeting extends Component {
render() {
return <h1>Hello, {this.props.name}!</h1>;
}
}

Reactは、画面に表示するものを判断する必要があるときは常に、renderメソッドを呼び出します。通常、そこからJSXを返します。renderメソッドは、純粋関数である必要があります。つまり、JSXのみを計算する必要があります。

関数コンポーネントと同様に、クラスコンポーネントも、親コンポーネントからpropsによって情報を受け取ることができます。ただし、propsを読み取るための構文は異なります。たとえば、親コンポーネントが<Greeting name="Taylor" />をレンダリングする場合、this.propsからname propをthis.props.nameのように読み取ることができます。

import { Component } from 'react';

class Greeting extends Component {
  render() {
    return <h1>Hello, {this.props.name}!</h1>;
  }
}

export default function App() {
  return (
    <>
      <Greeting name="Sara" />
      <Greeting name="Cahal" />
      <Greeting name="Edite" />
    </>
  );
}

useStateのようなuseで始まる関数)フックはクラスコンポーネント内ではサポートされていないことに注意してください。

落とし穴

コンポーネントはクラスではなく関数として定義することをお勧めします。移行方法はこちらをご覧ください。


クラスコンポーネントへのstateの追加

クラスにstateを追加するには、stateという名前のプロパティにオブジェクトを割り当てます。stateを更新するには、this.setStateを呼び出します。

import { Component } from 'react';

export default class Counter extends Component {
  state = {
    name: 'Taylor',
    age: 42,
  };

  handleNameChange = (e) => {
    this.setState({
      name: e.target.value
    });
  }

  handleAgeChange = () => {
    this.setState({
      age: this.state.age + 1 
    });
  };

  render() {
    return (
      <>
        <input
          value={this.state.name}
          onChange={this.handleNameChange}
        />
        <button onClick={this.handleAgeChange}>
          Increment age
        </button>
        <p>Hello, {this.state.name}. You are {this.state.age}.</p>
      </>
    );
  }
}

落とし穴

コンポーネントはクラスではなく関数として定義することをお勧めします。移行方法はこちらをご覧ください。


クラスコンポーネントへのライフサイクルメソッドの追加

クラスにはいくつかの特別なメソッドを定義できます。

componentDidMountメソッドを定義すると、コンポーネントが画面に追加(マウント)されたときにReactがそれを呼び出します。Reactは、propsまたはstateが変更されたためにコンポーネントが再レンダリングされた後、componentDidUpdateを呼び出します。Reactは、コンポーネントが画面から削除(アンマウント)された後、componentWillUnmountを呼び出します。

componentDidMount を実装する場合、バグを避けるために、通常は3つのライフサイクル全てを実装する必要があります。例えば、componentDidMount が何らかの state や props を読み込む場合、それらの変更を処理するために componentDidUpdate も実装する必要があります。そして、componentDidMount が行っていた処理をクリーンアップするために componentWillUnmount も実装する必要があります。

例えば、この ChatRoom コンポーネントは、チャット接続を props と state に同期させます。

import { Component } from 'react';
import { createConnection } from './chat.js';

export default class ChatRoom extends Component {
  state = {
    serverUrl: 'https://localhost:1234'
  };

  componentDidMount() {
    this.setupConnection();
  }

  componentDidUpdate(prevProps, prevState) {
    if (
      this.props.roomId !== prevProps.roomId ||
      this.state.serverUrl !== prevState.serverUrl
    ) {
      this.destroyConnection();
      this.setupConnection();
    }
  }

  componentWillUnmount() {
    this.destroyConnection();
  }

  setupConnection() {
    this.connection = createConnection(
      this.state.serverUrl,
      this.props.roomId
    );
    this.connection.connect();    
  }

  destroyConnection() {
    this.connection.disconnect();
    this.connection = null;
  }

  render() {
    return (
      <>
        <label>
          Server URL:{' '}
          <input
            value={this.state.serverUrl}
            onChange={e => {
              this.setState({
                serverUrl: e.target.value
              });
            }}
          />
        </label>
        <h1>Welcome to the {this.props.roomId} room!</h1>
      </>
    );
  }
}

開発環境で Strict Mode が有効になっている場合、React は componentDidMount を呼び出した直後に componentWillUnmount を呼び出し、その後再度 componentDidMount を呼び出すことに注意してください。これは、componentWillUnmount の実装を忘れていたり、そのロジックが componentDidMount の処理を完全に「反映」していない場合に気付くのに役立ちます。

落とし穴

コンポーネントはクラスではなく関数として定義することをお勧めします。移行方法をご覧ください。


エラー境界を使用したレンダリングエラーのキャッチ

デフォルトでは、レンダリング中にアプリケーションでエラーが発生した場合、React はその UI を画面から削除します。これを防ぐために、UI の一部をエラー境界でラップできます。エラー境界は、クラッシュした部分の代わりに、フォールバック UI (例えば、エラーメッセージ) を表示できる特別なコンポーネントです。

エラー境界コンポーネントを実装するには、エラーに対応して state を更新し、エラーメッセージをユーザーに表示できる static getDerivedStateFromError を指定する必要があります。また、オプションで、エラーを分析サービスに記録するなど、追加のロジックを追加するために componentDidCatch を実装することもできます。

class ErrorBoundary extends React.Component {
constructor(props) {
super(props);
this.state = { hasError: false };
}

static getDerivedStateFromError(error) {
// Update state so the next render will show the fallback UI.
return { hasError: true };
}

componentDidCatch(error, info) {
// Example "componentStack":
// in ComponentThatThrows (created by App)
// in ErrorBoundary (created by App)
// in div (created by App)
// in App
logErrorToMyService(error, info.componentStack);
}

render() {
if (this.state.hasError) {
// You can render any custom fallback UI
return this.props.fallback;
}

return this.props.children;
}
}

次に、コンポーネントツリーの一部をそれでラップできます。

<ErrorBoundary fallback={<p>Something went wrong</p>}>
<Profile />
</ErrorBoundary>

Profile またはその子コンポーネントでエラーが発生した場合、ErrorBoundary はそのエラーを「キャッチ」し、提供したエラーメッセージとともにフォールバック UI を表示し、エラーレポートサービスに本番環境エラーレポートを送信します。

すべてのコンポーネントを個別のエラー境界でラップする必要はありません。エラー境界の粒度について考えるときは、エラーメッセージを表示することが理にかなっている場所を検討してください。例えば、メッセージングアプリでは、会話リストの周りにエラー境界を配置するのが理にかなっています。個々のメッセージの周りに配置するのも理にかなっています。ただし、すべてのアバターの周りに境界を配置することは理にかなっていません。

注意

現在、関数コンポーネントとしてエラー境界を記述する方法はありません。ただし、エラー境界クラスを自分で記述する必要はありません。例えば、react-error-boundary を代わりに使用できます。


代替案

クラスから関数へのシンプルなコンポーネントの移行

通常、コンポーネントは関数として定義します。

例えば、この Greeting クラスコンポーネントを関数に変換するとします。

import { Component } from 'react';

class Greeting extends Component {
  render() {
    return <h1>Hello, {this.props.name}!</h1>;
  }
}

export default function App() {
  return (
    <>
      <Greeting name="Sara" />
      <Greeting name="Cahal" />
      <Greeting name="Edite" />
    </>
  );
}

Greeting という名前の関数を定義します。ここに、render 関数の本体を移動します。

function Greeting() {
// ... move the code from the render method here ...
}

this.props.name の代わりに、分割代入構文を使用して name prop を定義し、直接読み取ります。

function Greeting({ name }) {
return <h1>Hello, {name}!</h1>;
}

完全な例を次に示します。

function Greeting({ name }) {
  return <h1>Hello, {name}!</h1>;
}

export default function App() {
  return (
    <>
      <Greeting name="Sara" />
      <Greeting name="Cahal" />
      <Greeting name="Edite" />
    </>
  );
}


クラスから関数への状態を持つコンポーネントの移行

この Counter クラスコンポーネントを関数に変換するとします。

import { Component } from 'react';

export default class Counter extends Component {
  state = {
    name: 'Taylor',
    age: 42,
  };

  handleNameChange = (e) => {
    this.setState({
      name: e.target.value
    });
  }

  handleAgeChange = (e) => {
    this.setState({
      age: this.state.age + 1 
    });
  };

  render() {
    return (
      <>
        <input
          value={this.state.name}
          onChange={this.handleNameChange}
        />
        <button onClick={this.handleAgeChange}>
          Increment age
        </button>
        <p>Hello, {this.state.name}. You are {this.state.age}.</p>
      </>
    );
  }
}

必要な状態変数を使用して関数を宣言することから始めます。

import { useState } from 'react';

function Counter() {
const [name, setName] = useState('Taylor');
const [age, setAge] = useState(42);
// ...

次に、イベントハンドラーを変換します。

function Counter() {
const [name, setName] = useState('Taylor');
const [age, setAge] = useState(42);

function handleNameChange(e) {
setName(e.target.value);
}

function handleAgeChange() {
setAge(age + 1);
}
// ...

最後に、thisで始まるすべての参照を、コンポーネントで定義した変数と関数に置き換えます。例えば、this.state.ageageに、this.handleNameChangehandleNameChangeに置き換えます。

以下に、完全に変換されたコンポーネントを示します。

import { useState } from 'react';

export default function Counter() {
  const [name, setName] = useState('Taylor');
  const [age, setAge] = useState(42);

  function handleNameChange(e) {
    setName(e.target.value);
  }

  function handleAgeChange() {
    setAge(age + 1);
  }

  return (
    <>
      <input
        value={name}
        onChange={handleNameChange}
      />
      <button onClick={handleAgeChange}>
        Increment age
      </button>
      <p>Hello, {name}. You are {age}.</p>
    </>
  )
}


ライフサイクルメソッドを持つコンポーネントをクラスから関数に移行する

以下のライフサイクルメソッドを持つChatRoomクラスコンポーネントを関数に変換すると仮定します。

import { Component } from 'react';
import { createConnection } from './chat.js';

export default class ChatRoom extends Component {
  state = {
    serverUrl: 'https://localhost:1234'
  };

  componentDidMount() {
    this.setupConnection();
  }

  componentDidUpdate(prevProps, prevState) {
    if (
      this.props.roomId !== prevProps.roomId ||
      this.state.serverUrl !== prevState.serverUrl
    ) {
      this.destroyConnection();
      this.setupConnection();
    }
  }

  componentWillUnmount() {
    this.destroyConnection();
  }

  setupConnection() {
    this.connection = createConnection(
      this.state.serverUrl,
      this.props.roomId
    );
    this.connection.connect();    
  }

  destroyConnection() {
    this.connection.disconnect();
    this.connection = null;
  }

  render() {
    return (
      <>
        <label>
          Server URL:{' '}
          <input
            value={this.state.serverUrl}
            onChange={e => {
              this.setState({
                serverUrl: e.target.value
              });
            }}
          />
        </label>
        <h1>Welcome to the {this.props.roomId} room!</h1>
      </>
    );
  }
}

まず、componentWillUnmountcomponentDidMount の逆の動作をしていることを確認します。上記の例では、componentDidMount が設定した接続を切断しています。このようなロジックがない場合は、最初に追加してください。

次に、componentDidUpdate メソッドが componentDidMount で使用しているpropsとstateの変更を処理していることを確認します。上記の例では、componentDidMountsetupConnection を呼び出し、this.state.serverUrlthis.props.roomId を読み取ります。これが componentDidUpdatethis.state.serverUrlthis.props.roomId が変更されたかどうかをチェックし、変更された場合は接続をリセットする理由です。componentDidUpdate のロジックが欠落しているか、関連するすべての props と state の変更を処理していない場合は、まずそれを修正してください。

上記の例では、ライフサイクルメソッド内のロジックは、コンポーネントをReactの外部のシステム(チャットサーバー)に接続しています。コンポーネントを外部システムに接続するには、このロジックを単一のエフェクトとして記述します:

import { useState, useEffect } from 'react';

function ChatRoom({ roomId }) {
const [serverUrl, setServerUrl] = useState('https://localhost:1234');

useEffect(() => {
const connection = createConnection(serverUrl, roomId);
connection.connect();
return () => {
connection.disconnect();
};
}, [serverUrl, roomId]);

// ...
}

このuseEffectの呼び出しは、上記のライフサイクルメソッドのロジックと同等です。ライフサイクルメソッドが複数の無関係なことを行っている場合は、それらを複数の独立したエフェクトに分割します。以下は、実際に試せる完全な例です。

import { useState, useEffect } from 'react';
import { createConnection } from './chat.js';

export default function ChatRoom({ roomId }) {
  const [serverUrl, setServerUrl] = useState('https://localhost:1234');

  useEffect(() => {
    const connection = createConnection(serverUrl, roomId);
    connection.connect();
    return () => {
      connection.disconnect();
    };
  }, [roomId, serverUrl]);

  return (
    <>
      <label>
        Server URL:{' '}
        <input
          value={serverUrl}
          onChange={e => setServerUrl(e.target.value)}
        />
      </label>
      <h1>Welcome to the {roomId} room!</h1>
    </>
  );
}

注意

コンポーネントが外部システムと同期しない場合は、エフェクトが必要ない場合があります。


コンテキストを持つコンポーネントをクラスから関数に移行する

この例では、PanelButton クラスコンポーネントは、コンテキストthis.context:から読み取ります。

import { createContext, Component } from 'react';

const ThemeContext = createContext(null);

class Panel extends Component {
  static contextType = ThemeContext;

  render() {
    const theme = this.context;
    const className = 'panel-' + theme;
    return (
      <section className={className}>
        <h1>{this.props.title}</h1>
        {this.props.children}
      </section>
    );    
  }
}

class Button extends Component {
  static contextType = ThemeContext;

  render() {
    const theme = this.context;
    const className = 'button-' + theme;
    return (
      <button className={className}>
        {this.props.children}
      </button>
    );
  }
}

function Form() {
  return (
    <Panel title="Welcome">
      <Button>Sign up</Button>
      <Button>Log in</Button>
    </Panel>
  );
}

export default function MyApp() {
  return (
    <ThemeContext.Provider value="dark">
      <Form />
    </ThemeContext.Provider>
  )
}

関数コンポーネントに変換する場合は、this.contextuseContext の呼び出しに置き換えます。

import { createContext, useContext } from 'react';

const ThemeContext = createContext(null);

function Panel({ title, children }) {
  const theme = useContext(ThemeContext);
  const className = 'panel-' + theme;
  return (
    <section className={className}>
      <h1>{title}</h1>
      {children}
    </section>
  )
}

function Button({ children }) {
  const theme = useContext(ThemeContext);
  const className = 'button-' + theme;
  return (
    <button className={className}>
      {children}
    </button>
  );
}

function Form() {
  return (
    <Panel title="Welcome">
      <Button>Sign up</Button>
      <Button>Log in</Button>
    </Panel>
  );
}

export default function MyApp() {
  return (
    <ThemeContext.Provider value="dark">
      <Form />
    </ThemeContext.Provider>
  )
}