Component
Component は React コンポーネントの基底クラスであり、JavaScript のクラスとして定義されています。React は現在でもクラスコンポーネントをサポートしていますが、新しいコードでの使用は推奨されません。
class Greeting extends Component {
render() {
return <h1>Hello, {this.props.name}!</h1>;
}
}- リファレンス
Componentcontextpropsstateconstructor(props)componentDidCatch(error, info)componentDidMount()componentDidUpdate(prevProps, prevState, snapshot?)componentWillMount()componentWillReceiveProps(nextProps)componentWillUpdate(nextProps, nextState)componentWillUnmount()forceUpdate(callback?)getSnapshotBeforeUpdate(prevProps, prevState)render()setState(nextState, callback?)shouldComponentUpdate(nextProps, nextState, nextContext)UNSAFE_componentWillMount()UNSAFE_componentWillReceiveProps(nextProps, nextContext)UNSAFE_componentWillUpdate(nextProps, nextState)static contextTypestatic defaultPropsstatic getDerivedStateFromError(error)static getDerivedStateFromProps(props, state)
- 使用法
- 代替案
リファレンス
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>
);
}
}props
クラスコンポーネントに渡された props は this.props の形で利用できます。
class Greeting extends Component {
render() {
return <h1>Hello, {this.props.name}!</h1>;
}
}
<Greeting name="Taylor" />state
クラスコンポーネントの state は this.state の形で利用できます。state フィールドはオブジェクトでなければなりません。state を直接書き換えてはいけません。state を変更したい場合は、新しい state を引数にして setState を呼び出します。
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>
</>
);
}
}constructor(props)
コンストラクタは、クラスコンポーネントがマウント(画面に追加)される前に実行されます。通常 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 = () => {
// ...
}コンストラクタには副作用やサブスクリプション(subscription, イベント登録や外部データ購読)を一切含めてはいけません。
引数
props: コンポーネントの初期 props。
返り値
constructor は何も返してはいけません。
注意点
-
コンストラクタ内で副作用やサブスクリプションを実行しないでください。代わりに
componentDidMountを使用してください。 -
コンストラクタ内では、他のすべてのステートメントの前に
super(props)を呼び出す必要があります。これを行わないと、コンストラクタが実行されている間this.propsがundefinedになるため、混乱を招きバグの原因となる可能性があります。 -
this.stateを直接セットして良い唯一の場所がコンストラクタです。他のすべてのメソッド内では、代わりにthis.setState()を使用する必要があります。コンストラクタ内でsetStateを呼び出さないでください。 -
サーバレンダリングを使用する場合、コンストラクタもサーバ上で実行され、その後に
renderメソッドが続きます。ただし、componentDidMountやcomponentWillUnmountのようなライフサイクルメソッドはサーバ上では実行されません。 -
Strict Mode が有効の場合、React は開発環境において
constructorを 2 回呼び出し、2 つのインスタンスのうち 1 つを破棄します。これにより、constructor外に移動するべき偶発的な副作用に気づきやすくなります。
componentDidCatch(error, info)
componentDidCatch を定義すると、子コンポーネント(遠くの子を含む)がレンダー中にエラーをスローしたときに React がそれを呼び出します。これにより、本番環境でそのエラーをエラーレポートサービスにログとして記録することができます。
通常これは、エラーに反応して state を更新し、ユーザにエラーメッセージを表示するための static getDerivedStateFromError と一緒に使用されます。これらのメソッドを持つコンポーネントのことをエラーバウンダリ (Error Boundary) と呼びます。
引数
-
error: スローされたエラー。現実的には通常Errorのインスタンスになりますが、このことは保証されていません。JavaScript では文字列やnullすら含む、任意の値をthrowすることが許されているためです。 -
info: エラーに関する追加情報を含むオブジェクト。そのcomponentStackフィールドには、スローしたコンポーネントとそのすべての親コンポーネントの名前およびソース上の位置を含んだスタックトレースが含まれます。本番環境では、コンポーネント名はミニファイされています。本番環境用にエラーレポートを設定する場合は、通常の JavaScript エラースタックと同じように、ソースマップを使用してコンポーネントスタックをデコードできます。
返り値
componentDidCatch は何も返してはいけません。
注意点
-
以前は、UI を更新してフォールバックのエラーメッセージを表示するために
componentDidCatch内でsetStateを呼び出すことが一般的でした。これは非推奨となり、代わりにstatic getDerivedStateFromErrorを定義することが推奨されています。 -
React の本番用ビルドと開発用ビルドでは、
componentDidCatchがエラーを処理する方法がわずかに異なります。開発中は、エラーがwindowにまでバブルアップするため、window.onerrorやwindow.addEventListener('error', callback)といったコードがcomponentDidCatchによってキャッチされたエラーを捕まえることができます。一方で本番環境ではエラーはバブルアップしないため、祖先のエラーハンドラはcomponentDidCatchによって明示的にキャッチされなかったエラーのみを受け取ります。
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 は何も返してはいけません。
注意点
-
Strict Mode がオンの場合、React は開発中に
componentDidMountを呼び出し、直後にcomponentWillUnmountを呼び出し、そして再度componentDidMountを呼び出します。これにより、componentWillUnmountの実装を忘れた場合や、そのロジックがcomponentDidMountの挙動と正しく「鏡のように対応」していない場合に気づきやすくなります。 -
componentDidMountの中で直ちにsetStateを呼び出すことは可能ですが、可能な限り避けるべきです。これは追加のレンダーを引き起こしますが、ブラウザが画面を更新する前に発生します。このため、このケースではrenderが 2 回呼び出されはしますが、ユーザは中途半端な state を見ずに済みます。このパターンはしばしばパフォーマンスの問題を引き起こすため、注意して使用してください。ほとんどの場合、初期 state をconstructorで代入できるはずです。ただし、モーダルやツールチップのような場合は、何らかの DOM ノードの位置やサイズに依存する要素をレンダーするのに DOM ノードの測定を行うため、これが必要になることがあります。
componentDidUpdate(prevProps, prevState, snapshot?)
componentDidUpdate メソッドを定義すると、コンポーネントが更新後の props や state で再レンダーされた直後に React がそれを呼び出します。このメソッドは初回レンダーでは呼び出されません。
更新後に 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。prevPropsとthis.propsを比較して何が変わったかを判断します。 -
prevState: 更新前の state。prevStateとthis.stateを比較して何が変わったかを判断します。 -
snapshot:getSnapshotBeforeUpdateを実装した場合、snapshotにはそのメソッドから返された値が含まれます。それ以外の場合はundefinedになります。
返り値
componentDidUpdate は何も返してはいけません。
注意点
-
shouldComponentUpdateが定義されており、それがfalseを返す場合、componentDidUpdateは呼び出されません。 -
componentDidUpdate内のロジックは通常、this.propsとprevProps、this.stateとprevStateを比較する条件文でラップする必要があります。そうでなければ、無限ループを作り出すリスクがあります。 -
componentDidUpdateの中で直ちにsetStateを呼び出すことは可能ですが、可能な限り避けるべきです。これは追加のレンダーを引き起こしますが、ブラウザが画面を更新する前に発生します。このため、このケースではrenderが 2 回呼び出されはしますが、ユーザは中途半端な state を見ずに済みます。このパターンはしばしばパフォーマンスの問題を引き起こします。ただし、モーダルやツールチップのようなレアなケースでは、何らかの DOM ノードの位置やサイズに依存する要素をレンダーするのに DOM ノードの測定を行うため、これが必要になることがあります。
componentWillMount()
componentWillReceiveProps(nextProps)
componentWillUpdate(nextProps, nextState)
componentWillUnmount()
componentWillUnmount メソッドを定義すると、React はコンポーネントが画面から削除(アンマウント)される前にこれを呼び出します。ここがデータの取得をキャンセルしたり、サブスクリプションを削除するのに一般的な場所です。
componentWillUnmount 内のロジックは componentDidMount 内のロジックと「鏡のように対応」するべきです。例えば、componentDidMount がサブスクリプションの設定を行う場合、componentWillUnmount はそのサブスクリプションをクリーンアップするべきです。componentWillUnmount のクリーンアップロジックが props や state を読み取る場合、通常は古い props と state に対応するリソース(サブスクリプションなど)をクリーンアップするために componentDidUpdate も実装する必要があります。
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の挙動と正しく「鏡のように対応」していない場合に気づきやすくなります。
forceUpdate(callback?)
コンポーネントを強制的に再レンダーします。
通常、これは必要ありません。コンポーネントの render メソッドが this.props、this.state および this.context からのみ読み取りを行う場合、コンポーネント内またはその親で setState が呼び出されると自動的に再レンダーが発生します。しかし、コンポーネントの render メソッドが外部データソースから直接読み取りを行っている場合、そのデータソースが変更されたときに React にユーザインターフェースを更新するように指示する必要があります。forceUpdate はそれを行えるようにするためのものです。
あらゆる forceUpdate の使用は避け、render 内では this.props と this.state からのみ読み取るようにしてください。
引数
- 省略可能
callback: 指定された場合、React は更新がコミットされた後に、渡されたcallbackを呼び出します。
返り値
forceUpdate は何も返しません。
注意点
forceUpdateを呼び出すと、React はshouldComponentUpdateを呼び出さずに再レンダーします。
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>
);
}
}上記の例では、getSnapshotBeforeUpdate で直接 scrollHeight プロパティを読み取ることが重要です。render、UNSAFE_componentWillReceiveProps、または UNSAFE_componentWillUpdate で読み取ることは安全ではありません。これらのメソッドが呼び出されてから React が DOM を更新するまでに時間差がある可能性があるためです。
引数
-
prevProps: 更新前の props。prevPropsとthis.propsを比較して何が変わったかを判断します。 -
prevState: 更新前の state。prevStateとthis.stateを比較して何が変わったかを判断します。
返り値
任意の型のスナップショット値、または null を返してください。返した値は、componentDidUpdate の第 3 引数として渡されます。
注意点
shouldComponentUpdateが定義されており、falseを返す場合、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.props、this.state、および this.context を読み取ることができます。
render メソッドは純関数として書くべきです。つまり、props、state、コンテクストが同じであれば同じ結果を返すべきです。また、副作用(サブスクリプションの設定など)を含んだり、ブラウザの API とやり取りしたりするべきではありません。副作用は、イベントハンドラや componentDidMount のようなメソッド内で行うべきです。
引数
render は引数を受け取りません。
返り値
render はあらゆる有効な React ノードを返すことができます。これには、<div /> のような React 要素、文字列、数値、ポータル、空のノード(null、undefined、true、false)、および React ノードの配列が含まれます。
注意点
-
renderは props、state、コンテクストに対する純関数として書くべきです。副作用を持ってはいけません。 -
shouldComponentUpdateが定義されておりfalseを返す場合、renderは呼び出されません。 -
Strict Mode が有効の場合、React は開発中に
renderを 2 回呼び出し、そのうちの 1 つの結果を破棄します。これにより、renderメソッドの外に移動するべき偶発的な副作用に気づきやすくなります。 -
renderの呼び出しとその後のcomponentDidMountやcomponentDidUpdateの呼び出しとの間に一対一の対応関係はありません。React が必要と判断した場合、renderの呼び出し結果の一部は破棄される可能性があります。
setState(nextState, callback?)
setState の呼び出しは React コンポーネントの state の更新を行います。
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 はコンポーネントの state への更新をキューに入れます。これは、このコンポーネントとその子を新しい state で再レンダーする必要があることを React に伝えます。これが、ユーザ操作に対応してユーザインターフェースを更新するための主要な方法となります。
また、setState に関数を渡すこともできます。これにより、前の state に基づいて state を更新することができます。
handleIncreaseAge = () => {
this.setState(prevState => {
return {
age: prevState.age + 1
};
});
}こうする必要があるわけではありませんが、同じイベント中に複数回 state を更新したい場合には有用です。
引数
-
nextState:オブジェクトまたは関数。nextStateとしてオブジェクトを渡すと、それはthis.stateに浅く (shallow) マージされます。nextStateとして関数を渡すと、それは更新用関数 (updater function) として扱われます。その関数は純関数でなければならず、state と props の現在値を引数として取り、this.stateに浅くマージされるためのオブジェクトを返します。React はこの更新用関数をキューに入れてからコンポーネントを再レンダーします。次のレンダー中に、React は前の state に対してキューに入れられたすべての更新用関数を適用し、次回の state を計算します。
-
省略可能
callback: 指定された場合、React は更新がコミットされた後に渡されたcallbackを呼び出します。
返り値
setState は何も返しません。
注意点
-
setStateは即時的なコンポーネントの更新命令ではなく、リクエストだと考えてください。複数のコンポーネントがイベントに応じて state を更新するとき、React はそれらの更新をバッチ(束ね)処理し、イベントの終了時に 1 度だけ再レンダーします。特定の state 更新を強制的に同期的に適用する必要がある稀なケースでは、flushSyncでラップすることができますが、これはパフォーマンスを低下させる可能性があります。 -
setStateはthis.stateを即時に更新しません。このため、setStateを呼び出した直後にthis.stateを読み取ることは潜在的な落とし穴となります。代わりに、componentDidUpdateまたは setState のcallback引数を使用してください。どちらも更新が適用された後に発火することが保証されています。前の state に基づいた state を設定する必要がある場合、上記で説明したようにnextStateとして関数を渡すことができます。
shouldComponentUpdate(nextProps, nextState, nextContext)
shouldComponentUpdate を定義すると、React は再レンダーをスキップできるかどうかを判断するためにそれを呼び出します。
これを本当に手動で書きたい場合は、this.props と nextProps、this.state と nextState を比較した上で 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;
}
// ...
}新しい props や state を受け取る際に React は shouldComponentUpdate を呼び出します。デフォルトは true です。このメソッドは初回レンダーの場合と forceUpdate が使用された場合には呼び出されません。
引数
nextProps: コンポーネントがレンダーしようとしている次の props。何が変わったかを判断するためにnextPropsをthis.propsと比較します。nextState: コンポーネントがレンダーしようとしている次の state。何が変わったかを判断するためにnextStateをthis.stateと比較します。nextContext: コンポーネントがレンダーしようとしている次のコンテクスト。何が変わったかを判断するためにnextContextをthis.contextと比較します。static contextTypeを指定した場合のみ利用可能です。
返り値
コンポーネントを再レンダーしたい場合は true を返します。これがデフォルトの挙動です。
再レンダーがスキップ可能であると React に伝えるためには false を返します。
注意点
-
このメソッドはパフォーマンス最適化のためだけに存在しています。もし、このメソッドがないとコンポーネントが壊れる場合は、まずそれを修正してください。
-
shouldComponentUpdateを手書きする代わりに、PureComponentの使用を検討してください。PureComponentは props と state を浅く比較し、必要な更新までスキップしてしまう可能性を減らします。 -
shouldComponentUpdateで深い等価性チェックを行ったり、JSON.stringifyを使用したりすることはお勧めしません。これによりパフォーマンスがあらゆる props と state のデータ構造に依存するようになり、予測不可能になります。よくてもアプリケーションが数秒間フリーズするリスクがあり、最悪の場合はクラッシュする危険があります。 -
falseを返しても、子コンポーネントの state が変更された場合に子コンポーネントの再レンダーが抑止されるわけではありません。 -
falseを返しても、コンポーネントが再レンダーされないことが保証されるわけではありません。React は返り値をヒントとして使用しますが、他の理由でコンポーネントを再レンダーすることが理にかなっていると判断した場合、再レンダーを行うことがあります。
UNSAFE_componentWillMount()
UNSAFE_componentWillMount を定義すると、React は constructor の直後にそれを呼び出します。これは歴史的な理由で存在するものであり、新しいコードでは使用すべきではありません。代わりに以下の代替手段のいずれかを使用してください。
- state を初期化するためには、
stateをクラスフィールドとして宣言するか、constructorでthis.stateを設定します。 - 副作用を実行する必要があるか、サブスクリプションを設定する必要がある場合は、そのロジックを
componentDidMountに移動します。
引数
UNSAFE_componentWillMount は引数を受け取りません。
返り値
UNSAFE_componentWillMount は何も返してはいけません。
注意点
-
コンポーネントが
static getDerivedStateFromPropsまたはgetSnapshotBeforeUpdateを実装している場合、UNSAFE_componentWillMountは呼び出されません。 -
Suspenseのようなモダンな React 機能を使用しているアプリでは、UNSAFE_componentWillMountはその名前に反して、コンポーネントが本当にマウントされることを保証しません。(例えば子コンポーネントのコードがまだロードされていないなどの理由で)レンダーの試行がサスペンドした場合、React は進行中のツリーを破棄し、次の試行にてコンポーネントをゼロから構築しようとします。これがこのメソッドが “unsafe” となっている理由です。実際にマウントされたという事実に依存するコード(サブスクリプションの追加など)はcomponentDidMountに入れるべきです。 -
UNSAFE_componentWillMountはサーバレンダリング中に実行される唯一のライフサイクルメソッドです。現実的にはあらゆる利用目的においてこれはconstructorと同一であるため、このタイプのロジックにはconstructorを使用すべきです。
UNSAFE_componentWillReceiveProps(nextProps, nextContext)
UNSAFE_componentWillReceiveProps を定義すると、コンポーネントが新しい props を受け取るときに React がそれを呼び出します。これは歴史的な理由で存在するものであり、新しいコードでは使用すべきではありません。代わりに以下の代替手段のいずれかを使用してください。
- props の変更に応じて副作用を実行する必要がある場合(例えば、データの取得、アニメーションの実行、またはサブスクリプションの再初期化)、そのロジックを代わりに
componentDidUpdateに移動してください。 - 一部の props が変更されたときにあるデータの再計算を避ける必要がある場合、代わりにメモ化ヘルパーを使用してください。
- props が変更されたときにある state を「リセット」する必要がある場合、コンポーネントを完全に制御されたコンポーネントにするか、key 付きの非制御コンポーネントにすることを検討してください。
- props が変更されたときにある state を「調整」する必要がある場合、レンダー中に props のみから必要な情報をすべて計算できないか確認してください。できない場合は、代わりに
static getDerivedStateFromPropsを使用してください。
引数
nextProps: コンポーネントが親コンポーネントから受け取ろうとしている次の props。何が変更されたかを判断するためにnextPropsをthis.propsと比較します。nextContext: コンポーネントが最も近いプロバイダから受け取ろうとしている次のコンテクスト。何が変更されたかを判断するためにnextContextをthis.contextと比較します。static contextTypeを指定した場合のみ利用可能です。
返り値
UNSAFE_componentWillReceiveProps は何も返してはいけません。
注意点
-
static getDerivedStateFromPropsまたはgetSnapshotBeforeUpdateを実装しているコンポーネントでは、UNSAFE_componentWillReceivePropsは呼び出されません。 -
Suspenseのようなモダンな React 機能を使用しているアプリでは、UNSAFE_componentWillReceivePropsはその名前に反して、コンポーネントがその props を本当に受け取ることを保証しません。(例えば子コンポーネントのコードがまだロードされていないなどの理由で)レンダーの試行がサスペンドした場合、React は進行中のツリーを破棄し、次の試行にてコンポーネントをゼロから構築しようとします。次のレンダーの試行の時点では、props は異なっている可能性があります。これがこのメソッドが “unsafe” となっている理由です。コミットされた更新のみに対して実行すべきコード(サブスクリプションのリセットなど)は、componentDidUpdateに入れるべきです。 -
UNSAFE_componentWillReceivePropsが呼ばれてもコンポーネントが前回と異なる props を受け取ったことを意味するものではありません。何かが変わったかどうかを確認するにはnextPropsとthis.propsを自分で比較する必要があります。 -
React はマウント時に
UNSAFE_componentWillReceivePropsを初期 props を引数に呼び出すことはありません。このメソッドは、コンポーネントの props の一部が更新される予定の場合にのみ呼び出されます。例えば、setStateを呼び出しても、一般的には同じコンポーネント内のUNSAFE_componentWillReceivePropsはトリガされません。
UNSAFE_componentWillUpdate(nextProps, nextState)
UNSAFE_componentWillUpdate を定義すると、新しい props または state でレンダーする前に React がそれを呼び出します。これは歴史的な理由で存在するものであり、新しいコードでは使用すべきではありません。代わりに以下の代替手段のいずれかを使用してください。
- prop または state の変更に応じて副作用(例えば、データの取得、アニメーションの実行、サブスクリプションの再初期化など)を実行する必要がある場合、そのロジックを
componentDidUpdateに移動してください。 - DOM から情報を読み取り(例えば、現在のスクロール位置を保存するなど)、それを後で
componentDidUpdateで使用する場合は、getSnapshotBeforeUpdate内で読み取るようにしてください。
引数
nextProps: コンポーネントが次にレンダーする予定の props。何が変わったかを判断するために、nextPropsとthis.propsを比較します。nextState: コンポーネントが次にレンダーする予定の state。何が変わったかを判断するために、nextStateとthis.stateを比較します。
返り値
UNSAFE_componentWillUpdate は何も返してはいけません。
注意点
-
shouldComponentUpdateが定義されておりfalseを返す場合、UNSAFE_componentWillUpdateは呼び出されません。 -
コンポーネントが
static getDerivedStateFromPropsまたはgetSnapshotBeforeUpdateを実装している場合、UNSAFE_componentWillUpdateは呼び出されません。 -
componentWillUpdateの呼び出し中にsetState(または最終的にsetStateが呼び出されるようなあらゆるメソッド、例えば Redux アクションのディスパッチ)を呼び出すことはサポートされていません。 -
Suspenseのようなモダンな React 機能を使用しているアプリでは、UNSAFE_componentWillUpdateはその名前に反して、コンポーネントが本当に更新されることを保証しません。(例えば子コンポーネントのコードがまだロードされていないなどの理由で)レンダーの試行がサスペンドした場合、React は進行中のツリーを破棄し、次の試行にてコンポーネントをゼロから構築しようとします。次のレンダーの試行の時点では props と state は異なっている可能性があります。これがこのメソッドが “unsafe” となっている理由です。コミットされた更新のみに対して実行すべきコード(サブスクリプションのリセットなど)は、componentDidUpdateに入れるべきです。 -
UNSAFE_componentWillUpdateが呼ばれてもコンポーネントが前回とは異なる props や state を受け取ったことを意味するものではありません。何かが変わったかどうかを確認するには、nextPropsとthis.props、nextStateとthis.stateを自分で比較する必要があります。 -
React は props や state の初期値を使ってマウント時に
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>
);
}
}static defaultProps
クラスのデフォルトの props を設定するために static defaultProps を定義できます。これらは undefined および存在しない props に対して使用されますが、null である props には使用されません。
例えば以下のようにして、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" />
</>static getDerivedStateFromError(error)
static getDerivedStateFromError を定義すると、子コンポーネント(遠くの子を含む)がレンダー中にエラーをスローしたときに React がそれを呼び出します。これにより、UI をクリアする代わりにエラーメッセージを表示できます。
通常これは、エラーレポートを何らかの分析サービスに送信できるようにするための componentDidCatch と一緒に使用されます。これらのメソッドを持つコンポーネントのことをエラーバウンダリと呼びます。
引数
error: スローされたエラー。現実的には通常Errorのインスタンスになりますが、このことは保証されていません。JavaScript では文字列やnullすら含む、任意の値をthrowすることが許されているためです。
返り値
static getDerivedStateFromError からは、エラーメッセージを表示するようコンポーネントに指示するための state を返します。
注意点
static getDerivedStateFromErrorは純関数であるべきです。副作用(例えば、分析サービスの呼び出し)を実行したい場合は、componentDidCatchも実装する必要があります。
static getDerivedStateFromProps(props, state)
static getDerivedStateFromProps を定義すると、React は初回のマウントとその後の更新の両方で、render を呼び出す直前にこれを呼び出します。state を更新するためのオブジェクトを返すか、何も更新しない場合は null を返すようにします。
このメソッドは、state が props の経時的な変化に依存するという稀なユースケースのために存在します。例えば、この Form コンポーネントは props である userID が変更されたときに state である 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;
}
// ...
}このパターンでは、(userID のような)props の前回の値を、(prevUserID のような)state に保持する必要があることに注意してください。
引数
props: コンポーネントがレンダーしようとしている次の props。state: コンポーネントがレンダーしようとしている次の state。
返り値
static getDerivedStateFromProps は、state を更新するためのオブジェクトを返すか、何も更新しない場合は null を返します。
注意点
-
このメソッドは、原因に関係なくすべてのレンダーで発火します。
UNSAFE_componentWillReceivePropsはこれとは異なり、親が再レンダーを引き起こしたときのみ発火し、ローカルのsetStateの結果としては発火しません。 -
このメソッドはコンポーネントのインスタンスにアクセスできません。お望みであれば、コンポーネントの props と state に対する純関数をクラス定義の外部に抽出することで、
static getDerivedStateFromPropsと他のクラスメソッドとの間でコードを再利用することができます。
使用法
クラスコンポーネントを定義する
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" /> をレンダーする場合、この name プロパティは this.props から 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" /> </> ); }
フック(use で始まる useState のような関数)はクラスコンポーネント内ではサポートされていないことに注意してください。
クラスコンポーネントに 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 がそれを呼び出します。props や state の変更によりコンポーネントが再レンダーされた後に、React は 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 を実装することもできます。
captureOwnerStack を使うことで開発中にオーナーのスタックトレースを含めることが可能です。
import * as React from 'react';
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) {
logErrorToMyService(
error,
// Example "componentStack":
// in ComponentThatThrows (created by App)
// in ErrorBoundary (created by App)
// in div (created by App)
// in App
info.componentStack,
// Warning: `captureOwnerStack` is not available in production.
React.captureOwnerStack(),
);
}
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 を表示し、エラーレポートをあなたのエラーレポーティングサービスに送信します。
すべてのコンポーネントを別々のエラーバウンダリでラップする必要はありません。エラーバウンダリの粒度について考える際は、エラーメッセージをどこに表示するのが理にかなっているかを考えてみてください。例えば、メッセージングアプリでは、会話のリストをエラーバウンダリで囲むのが理にかなっています。また、メッセージを個別に囲むことも理にかなっているでしょう。しかし、アバターを 1 つずつ囲むことには意味がありません。
代替案
シンプルなコンポーネントをクラスから関数へ移行
一般的にはコンポーネントは関数として定義します。
例えば、この 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 とする代わりに、props である name は分割代入構文を使用して定義し、直接読み取ります:
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" /> </> ); }
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> </> ); } }
まず、必要となる state 変数 を持った関数を宣言します。
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.age を age に、this.handleNameChange を handleNameChange に置き換えます。
完全に書き換えられたコンポーネントはこちらです。
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> </> ) }
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> </> ); } }
まずは componentWillUnmount が componentDidMount の逆の動作をしていることを確認してください。上記の例では componentDidMount によって確立された接続を切断しているので、そうなっています。そのようなロジックが存在しない場合、まずそれを追加してください。
次に、componentDidUpdate メソッドが、componentDidMount で使用している props と state の変更を処理していることを確認してください。上記の例では、componentDidMount は setupConnection を呼び出しており、それが this.state.serverUrl と this.props.roomId を読み取っています。したがって componentDidUpdate も this.state.serverUrl と this.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> </> ); }
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 value="dark"> <Form /> </ThemeContext> ) }
これらを関数コンポーネントに変換する場合、this.context を useContext の呼び出しに置き換えます。
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 value="dark"> <Form /> </ThemeContext> ) }