コンポーネント
Component は、JavaScript クラスとして定義される React コンポーネントの基本クラスです。クラスコンポーネントは 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を変更したい場合は、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>
</>
);
}
}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.propsがundefinedになり、混乱を招きバグの原因となる可能性があります。 -
コンストラクターは、
this.stateを直接代入できる唯一の場所です。他のすべてのメソッドでは、代わりにthis.setState()を使用する必要があります。コンストラクターでsetStateを呼び出さないでください。 -
サーバーレンダリングを使用する場合、コンストラクターはサーバー上でも実行され、その後に
renderメソッドが続きます。ただし、componentDidMountやcomponentWillUnmountのようなライフサイクルメソッドはサーバー上では実行されません。 -
StrictModeがオンの場合、Reactは開発時に
constructorを2回呼び出し、そのうちの1つのインスタンスを破棄します。これは、constructorの外に移動する必要がある偶発的な副作用に気づくのに役立ちます。
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によって明示的にキャッチされなかったエラーのみを受け取ります。
componentDidMount()
componentDidMountメソッドを定義すると、Reactはコンポーネントが画面に追加(マウント)されたときにそれを呼び出します。これは、データフェッチを開始したり、サブスクリプションを設定したり、DOMノードを操作したりする一般的な場所です。
componentDidMountを実装する場合は、バグを回避するために、通常、他のライフサイクルメソッドを実装する必要があります。たとえば、componentDidMountが一部のstateまたはpropsを読み取る場合は、変更を処理するためにcomponentDidUpdateも実装し、componentDidMountが行っていたことをクリーンアップするためにcomponentWillUnmountも実装する必要があります。
class ChatRoom extends Component {
state = {
serverUrl: 'https://: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 ノードを測定する必要がある場合は、必要になることがあります。
componentDidUpdate(prevProps, prevState, snapshot?)
componentDidUpdate メソッドを定義した場合、React は、コンポーネントが更新された props または state で再レンダリングされた直後にそれを呼び出します。このメソッドは、初期レンダリングでは呼び出されません。
更新後に DOM を操作するために使用できます。また、現在の props と前の props を比較する限り (たとえば、props が変更されていない場合はネットワークリクエストが必要ない場合があります)、ネットワークリクエストを実行する一般的な場所でもあります。通常は、componentDidMount および componentWillUnmount と組み合わせて使用します:
class ChatRoom extends Component {
state = {
serverUrl: 'https://: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回呼び出されるとしても、ユーザーは中間状態を見ないことが保証されます。このパターンはしばしばパフォーマンスの問題を引き起こしますが、サイズや位置に依存する何かをレンダリングする前にDOMノードを測定する必要がある場合、モーダルやツールチップなどのまれなケースで必要になることがあります。
componentWillMount()
componentWillReceiveProps(nextProps)
componentWillUpdate(nextProps, nextState)
componentWillUnmount()
componentWillUnmount メソッドを定義すると、Reactは、コンポーネントが画面から削除される(アンマウントされる)前にそれを呼び出します。これは、データフェッチをキャンセルしたり、サブスクリプションを削除したりする一般的な場所です。
componentWillUnmount 内のロジックは、componentDidMount 内のロジックを「ミラーリング」する必要があります。たとえば、componentDidMount がサブスクリプションを設定する場合、componentWillUnmount はそのサブスクリプションをクリーンアップする必要があります。componentWillUnmount のクリーンアップロジックが一部のpropsまたはstateを読み取る場合、通常は componentDidUpdate を実装して、古いpropsおよびstateに対応するリソース(サブスクリプションなど)をクリーンアップする必要もあります。
class ChatRoom extends Component {
state = {
serverUrl: 'https://: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>
);
}
}上記の例では、scrollHeight プロパティを getSnapshotBeforeUpdate 内で直接読み取ることが重要です。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、および context が同じであれば、同じ結果を返す必要があります。また、(サブスクリプションの設定などの) 副作用を含めたり、ブラウザ API と対話したりしないでください。副作用は、イベントハンドラーまたは componentDidMount などのメソッドで発生する必要があります。
パラメータ
render はパラメータを受け取りません。
戻り値
render は、有効なReactノードであればどのようなものでも返すことができます。これには、<div />のようなReact要素、文字列、数値、ポータル、空のノード(null、undefined、true、および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に関数を渡すこともできます。これにより、前の状態に基づいて状態を更新できます
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でラップできますが、パフォーマンスが低下する可能性があります。 -
setStateはthis.stateをすぐに更新しません。これにより、setStateを呼び出した直後にthis.stateを読むと、潜在的な落とし穴になります。代わりに、componentDidUpdateまたはsetStateのcallback引数を使用してください。どちらも、更新が適用された後に発火することが保証されています。前の状態に基づいて状態を設定する必要がある場合は、上記のように、関数を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;
}
// ...
}React は、新しい props または state が受信されたときにレンダリングする前に shouldComponentUpdate を呼び出します。デフォルトは true です。このメソッドは、最初のレンダリング時や、forceUpdate が使用された場合には呼び出されません。
パラメータ
nextProps:コンポーネントがこれからレンダリングする props です。nextPropsをthis.propsと比較して、何が変更されたかを判断します。nextState:コンポーネントがこれからレンダリングする state です。nextStateをthis.stateと比較して、何が変更されたかを判断します。nextContext:コンポーネントがこれからレンダリングする context です。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は呼び出されません。 -
名前が示すにもかかわらず、
UNSAFE_componentWillMountは、アプリがSuspenseのような最新のReact機能を使用している場合、コンポーネントがマウントされることを保証しません。レンダリングの試行が中断された場合(たとえば、一部の子コンポーネントのコードがまだロードされていない場合)、Reactは進行中のツリーを破棄し、次の試行時にコンポーネントを最初から構築しようとします。これがこのメソッドが「安全でない」理由です。(サブスクリプションの追加など)マウントに依存するコードは、componentDidMountに記述する必要があります。 -
UNSAFE_componentWillMountは、サーバーレンダリング中に実行される唯一のライフサイクルメソッドです。実際上、constructorと同一であるため、この種のロジックには代わりにconstructorを使用する必要があります。
UNSAFE_componentWillReceiveProps(nextProps, nextContext)
UNSAFE_componentWillReceivePropsを定義すると、コンポーネントが新しいpropsを受け取るときにReactによって呼び出されます。これは歴史的な理由でのみ存在し、新しいコードでは使用しないでください。代わりに、代替手段のいずれかを使用してください。
- propsの変更に応じて副作用を実行する必要がある場合(たとえば、データをフェッチしたり、アニメーションを実行したり、サブスクリプションを再初期化したりする場合)、そのロジックを代わりに
componentDidUpdateに移動してください。 - propsが変更された場合にのみ一部のデータの再計算を避ける必要がある場合は、代わりにメモ化ヘルパーを使用してください。
- propsが変更されたときに一部の状態を「リセット」する必要がある場合は、コンポーネントを完全に制御されたコンポーネントにするか、またはキー付きで完全に制御されないコンポーネントにすることを検討してください。
- propsが変更されたときに一部の状態を「調整」する必要がある場合は、レンダリング中にpropsのみから必要なすべての情報を計算できるかどうかを確認してください。できない場合は、代わりに
static getDerivedStateFromPropsを使用してください。
パラメータ
nextProps: コンポーネントが親コンポーネントから受け取ろうとしている次の props です。this.propsとnextPropsを比較して、何が変更されたかを判断します。nextContext: コンポーネントが最も近いプロバイダーから受け取ろうとしている次のコンテキストです。this.contextとnextContextを比較して、何が変更されたかを判断します。static contextTypeを指定した場合にのみ利用可能です。
戻り値
UNSAFE_componentWillReceiveProps は何も返す必要はありません。
注意点
-
コンポーネントが
static getDerivedStateFromPropsまたはgetSnapshotBeforeUpdateを実装している場合、UNSAFE_componentWillReceivePropsは呼び出されません。 -
その名前にもかかわらず、
UNSAFE_componentWillReceivePropsは、アプリがSuspenseのような最新の React 機能を使用している場合、コンポーネントがそれらの props を受け取ることを保証するものではありません。レンダリングの試行が中断された場合(たとえば、一部の子コンポーネントのコードがまだロードされていない場合)、React は進行中のツリーを破棄し、次の試行時にコンポーネントを最初から作成しようとします。次のレンダリング試行時までに、props が異なる可能性があります。これが、このメソッドが「安全でない」理由です。(サブスクリプションのリセットなど)コミットされた更新に対してのみ実行する必要があるコードは、代わりにcomponentDidUpdateに移動する必要があります。 -
UNSAFE_componentWillReceivePropsは、コンポーネントが前回と異なる props を受け取ったことを意味するものではありません。変更があったかどうかを確認するには、自分でnextPropsとthis.propsを比較する必要があります。 -
React は、マウント時の初期 props で
UNSAFE_componentWillReceivePropsを呼び出しません。コンポーネントの props の一部が更新される場合にのみ、このメソッドを呼び出します。たとえば、setStateを呼び出しても、通常は同じコンポーネント内でUNSAFE_componentWillReceivePropsはトリガーされません。
UNSAFE_componentWillUpdate(nextProps, nextState)
UNSAFE_componentWillUpdate を定義すると、React は新しい props または state でレンダリングする前にそれを呼び出します。これは歴史的な理由でのみ存在し、新しいコードで使用すべきではありません。代わりに、代替案のいずれかを使用してください。
- props または state の変更に応じて副作用(たとえば、データのフェッチ、アニメーションの実行、またはサブスクリプションの再初期化)を実行する必要がある場合は、代わりにそのロジックを
componentDidUpdateに移動します。 componentDidUpdateで後で使用できるように、DOM から何らかの情報を(たとえば、現在のスクロール位置を保存するために)読み取る必要がある場合は、代わりにgetSnapshotBeforeUpdate内で読み取ります。
パラメータ
nextProps:コンポーネントがこれからレンダリングする props です。nextPropsをthis.propsと比較して、何が変更されたかを判断します。nextState: コンポーネントがレンダリングしようとしている次の state です。this.stateとnextStateを比較して、何が変更されたかを判断します。
以下を返します:
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 を受け取ったという意味ではありません。何か変更があったかどうかを確認するには、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 には使用されません。
たとえば、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 と一緒に使用され、エラーレポートを何らかの分析サービスに送信できます。これらのメソッドを持つコンポーネントは、エラー境界と呼ばれます。
パラメーター
戻り値
static getDerivedStateFromError は、エラーメッセージを表示するようにコンポーネントに指示する状態を返す必要があります。
注意点
static getDerivedStateFromErrorは純粋な関数である必要があります。副作用(たとえば、分析サービスを呼び出すなど)を実行する場合は、componentDidCatchも実装する必要があります。
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と他のクラスメソッドの間でコードを再利用できます。
使用法
クラスコンポーネントの定義
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://: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 を表示し、エラーレポートサービスに本番環境エラーレポートを送信します。
すべてのコンポーネントを個別のエラー境界でラップする必要はありません。エラー境界の粒度について考えるときは、エラーメッセージを表示することが理にかなっている場所を検討してください。例えば、メッセージングアプリでは、会話リストの周りにエラー境界を配置するのが理にかなっています。個々のメッセージの周りに配置するのも理にかなっています。ただし、すべてのアバターの周りに境界を配置することは理にかなっていません。
代替案
クラスから関数へのシンプルなコンポーネントの移行
通常、コンポーネントは関数として定義します。
例えば、この 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" /> </> ); }
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.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://: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://: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://: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> ) }