コンポーネント
Component
は、JavaScript クラスとして定義される React コンポーネントの基本クラスです。クラスコンポーネントは 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を変更したい場合は、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://localhost:1234'
};
componentDidMount() {
this.setupConnection();
}
componentDidUpdate(prevProps, prevState) {
if (
this.props.roomId !== prevProps.roomId ||
this.state.serverUrl !== prevState.serverUrl
) {
this.destroyConnection();
this.setupConnection();
}
}
componentWillUnmount() {
this.destroyConnection();
}
// ...
}
パラメータ
componentDidMount
はパラメータを受け取りません。
戻り値
componentDidMount
は何も返すべきではありません。
注意点
-
StrictMode が有効な開発環境では、React は
componentDidMount
を呼び出した後、すぐにcomponentWillUnmount
を呼び出し、再度componentDidMount
を呼び出します。これは、componentWillUnmount
の実装を忘れていないか、そのロジックがcomponentDidMount
の動作を完全に「反映」していない場合に気づくのに役立ちます。 -
componentDidMount
内ですぐにsetState
を呼び出しても構いませんが、可能な限り避けるのが最善です。これにより追加のレンダリングが発生しますが、ブラウザが画面を更新する前に発生します。これにより、この場合render
が 2 回呼び出されるとしても、ユーザーは中間の状態を見ることがないことが保証されます。このパターンはパフォーマンスの問題を引き起こすことが多いため、注意して使用してください。ほとんどの場合、代わりにconstructor
で初期状態を割り当てるべきです。ただし、モーダルやツールチップのように、サイズや位置に依存するものをレンダリングする前に DOM ノードを測定する必要がある場合は、必要になることがあります。
componentDidUpdate(prevProps, prevState, snapshot?)
componentDidUpdate
メソッドを定義した場合、React は、コンポーネントが更新された props または state で再レンダリングされた直後にそれを呼び出します。このメソッドは、初期レンダリングでは呼び出されません。
更新後に DOM を操作するために使用できます。また、現在の props と前の props を比較する限り (たとえば、props が変更されていない場合はネットワークリクエストが必要ない場合があります)、ネットワークリクエストを実行する一般的な場所でもあります。通常は、componentDidMount
および componentWillUnmount
と組み合わせて使用します:
class ChatRoom extends Component {
state = {
serverUrl: 'https://localhost:1234'
};
componentDidMount() {
this.setupConnection();
}
componentDidUpdate(prevProps, prevState) {
if (
this.props.roomId !== prevProps.roomId ||
this.state.serverUrl !== prevState.serverUrl
) {
this.destroyConnection();
this.setupConnection();
}
}
componentWillUnmount() {
this.destroyConnection();
}
// ...
}
パラメータ
-
prevProps
: 更新前の props。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://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>
);
}
}
上記の例では、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://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 を表示し、エラーレポートサービスに本番環境エラーレポートを送信します。
すべてのコンポーネントを個別のエラー境界でラップする必要はありません。エラー境界の粒度について考えるときは、エラーメッセージを表示することが理にかなっている場所を検討してください。例えば、メッセージングアプリでは、会話リストの周りにエラー境界を配置するのが理にかなっています。個々のメッセージの周りに配置するのも理にかなっています。ただし、すべてのアバターの周りに境界を配置することは理にかなっていません。
代替案
クラスから関数へのシンプルなコンポーネントの移行
通常、コンポーネントは関数として定義します。
例えば、この 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://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> ) }