Component
Component
は React コンポーネントの基底クラスであり、JavaScript のクラスとして定義されています。React は現在でもクラスコンポーネントをサポートしていますが、新しいコードでの使用は推奨されません。
class Greeting extends Component {
render() {
return <h1>Hello, {this.props.name}!</h1>;
}
}
- リファレンス
Component
context
props
state
constructor(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 contextType
static defaultProps
static 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
を実装することもできます。
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 を表示し、エラーレポートをあなたのエラーレポーティングサービスに送信します。
すべてのコンポーネントを別々のエラーバウンダリでラップする必要はありません。エラーバウンダリの粒度について考える際は、エラーメッセージをどこに表示するのが理にかなっているかを考えてみてください。例えば、メッセージングアプリでは、会話のリストをエラーバウンダリで囲むのが理にかなっています。また、メッセージを個別に囲むことも理にかなっているでしょう。しかし、アバターを 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.Provider value="dark"> <Form /> </ThemeContext.Provider> ) }
これらを関数コンポーネントに変換する場合、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.Provider value="dark"> <Form /> </ThemeContext.Provider> ) }