それを知ったのはリアクトv15.3.0、新しい基本クラスがありますピュアコンポーネント延長するピュアレンダーミックスイン組み込み。私が理解しているのは、内部的には、プロパティの浅い比較が採用されているということですshouldComponentUpdate
。
現在、React コンポーネントを定義する方法は 3 つあります。
- クラスを拡張しない機能的なステートレスコンポーネント
PureComponent
クラスを拡張するコンポーネントComponent
クラスを拡張する通常のコンポーネント
少し前までは、ステートレス コンポーネントを Pure コンポーネント、あるいは Dumb コンポーネントと呼んでいました。React では「Pure」という言葉の定義全体が変わったようです。
これら3つの基本的な違いは理解しているが、まだよく分からない。いつ何を選択するかまた、それぞれのパフォーマンスへの影響とトレードオフは何ですか?
アップデート:
私が明確にしたいと考えている質問は次のとおりです:
- 単純なコンポーネントを関数型として定義する (単純化のため) か、
PureComponent
クラスを拡張する (パフォーマンスのため) のどちらを選択すべきでしょうか? - 得られるパフォーマンスの向上は、失われたシンプルさとの本当のトレードオフでしょうか?
Component
パフォーマンスを向上させるために常に使用できる場合、通常のクラスを拡張する必要があるでしょうかPureComponent
?
ベストアンサー1
コンポーネントの目的/サイズ/プロパティ/動作に基づいて、これら 3 つをどのように決定、選択するのでしょうか?
React.PureComponent
またはReact.Component
カスタム メソッドを使用して を拡張するとshouldComponentUpdate
、パフォーマンスに影響が出ます。ステートレスな機能コンポーネントの使用は「アーキテクチャ」上の選択であり、すぐに使用できるパフォーマンス上の利点はありません (まだ)。
簡単に再利用できる、プレゼンテーションのみのシンプルなコンポーネントの場合は、ステートレスな機能コンポーネントを優先します。こうすることで、実際のアプリロジックから切り離され、テストが非常に簡単になり、予期しない副作用が発生しないことが保証されます。例外は、何らかの理由でたくさん
shouldComponentUpdate
または、レンダリング メソッドを本当に最適化する必要がある場合 (ステートレスな機能コンポーネントでは定義できないため)。出力が単純なプロパティ/状態 (PureComponent は浅い比較を実行するため、「単純」とはネストされたデータ構造がないことを意味します) に依存している
PureComponent
ことがわかっており、パフォーマンスの向上が必要であるか、または向上できる場合は拡張します。Component
次の/現在のプロパティと状態の間でカスタム比較ロジックを実行してパフォーマンスを向上させる必要がある場合は、独自のものを拡張して実装しますshouldComponentUpdate
。たとえば、lodash#isEqual を使用して詳細な比較をすばやく実行できます。class MyComponent extends Component { shouldComponentUpdate (nextProps, nextState) { return !_.isEqual(this.props, nextProps) || !_.isEqual(this.state, nextState); } }
また、独自の実装shouldComponentUpdate
や拡張はPureComponent
最適化であり、通常どおり、パフォーマンスの問題がある場合のみ検討を開始する必要があります(早すぎる最適化を避ける)。経験則として、私は常に、アプリケーションが動作状態になり、ほとんどの機能がすでに実装された後に、これらの最適化を行うようにしています。パフォーマンスの問題が実際に邪魔になったときに、それに集中する方がはるかに簡単です。
詳細情報
機能的なステートレス コンポーネント:
これらは関数を使用して定義されます。ステートレス コンポーネントには内部状態がないため、出力 (レンダリングされる内容) はこの関数への入力として指定されたプロパティのみに依存します。
長所:
React でコンポーネントを定義する最も簡単な方法です。状態を管理する必要がない場合は、クラスや継承に煩わされる必要はありません。関数とクラスの主な違いの 1 つは、関数の場合、出力が入力のみに依存する (以前の実行履歴には依存しない) ことが確実であることです。
理想的には、アプリでは、できるだけ多くのステートレス コンポーネントを持つことを目指す必要があります。これは通常、ロジックをビュー レイヤーの外側に移動し、それを redux のようなものに移動することを意味し、何もレンダリングせずに実際のロジックをテストできることを意味します (テストがはるかに簡単になり、再利用性が向上します)。
短所:
ライフサイクル メソッドはありません。他のフレンドを定義する方法はありません
componentDidMount
。通常は、階層の上位にある親コンポーネント内でこれを実行し、すべての子をステートレスなものに変えることができます。を定義できないため、再レンダリングが必要かどうかを手動で制御する方法はありません
shouldComponentUpdate
。コンポーネントが新しいプロパティを受け取るたびに再レンダリングが行われます (浅い比較などを行う方法はありません)。将来的には、React はステートレス コンポーネントを自動的に最適化できるようになりますが、現時点では使用できるライブラリがいくつかあります。ステートレス コンポーネントは単なる関数であるため、基本的には「関数のメモ化」という古典的な問題です。参照はサポートされていません:https://github.com/facebook/react/issues/4936
PureComponent クラスを拡張するコンポーネント VS Component クラスを拡張する通常のコンポーネント:
React には、構文をPureRenderMixin
使用して定義されたクラスにアタッチできる がありReact.createClass
ました。ミックスインは、shouldComponentUpdate
次のプロパティと次の状態の間で浅い比較を実行して、何か変更があったかどうかを確認する を定義するだけです。何も変更がない場合は、再レンダリングを実行する必要はありません。
ES6 構文を使用する場合、ミックスインは使用できません。そのため、便宜上、React は をPureComponent
使用する代わりに を継承できるクラスを導入しましたComponent
。は と同じようにPureComponent
実装するだけです。これは主に利便性のためであり、自分で実装する必要はありません。現在の状態/次の状態とプロパティの浅い比較は、パフォーマンスをすばやく向上できる最も一般的なシナリオである可能性があります。shouldComponentUpdate
PureRendererMixin
例:
class UserAvatar extends Component {
render() {
return <div><img src={this.props.imageUrl} /> {{ this.props.username }} </div>
}
}
ご覧のとおり、出力は と に依存しますprops.imageUrl
。props.username
親コンポーネントで<UserAvatar username="fabio" imageUrl="http://foo.com/fabio.jpg" />
同じプロパティを使用してレンダリングすると、render
出力がまったく同じであっても、React は毎回 を呼び出します。ただし、React は dom diff を実装しているため、DOM は実際には更新されないことに注意してください。それでも、dom diff の実行はコストがかかる可能性があるため、このシナリオでは無駄になります。
UserAvatar
代わりにコンポーネントが拡張される場合PureComponent
、浅い比較が実行されます。また、props と nextProps は同じであるため、render
まったく呼び出されません。
React における「純粋」の定義に関する注記:
一般的に、「純粋関数」とは、同じ入力が与えられた場合に常に同じ結果に評価される関数です。出力 (React の場合、メソッドによって返されるものrender
) は、履歴や状態に依存せず、副作用 (関数の外部の「世界」を変更する操作) もありません。
this.setState
React では、 を呼び出さず、 を使用しないコンポーネントを「ステートレス」と呼ぶ場合、上記の定義によれば、ステートレス コンポーネントは必ずしも純粋なコンポーネントであるとは限りませんthis.state
。
実際、 ではPureComponent
、ライフサイクル メソッドの実行中に副作用を実行することができます。たとえば、 内で Ajax リクエストを送信したりcomponentDidMount
、 内の div の高さを動的に調整するために DOM 計算を実行したりできますrender
。
「ダム コンポーネント」の定義には、より「実用的な」意味があります (少なくとも私の理解では)。つまり、ダム コンポーネントは、props を介して親コンポーネントから何をすべきかを「指示」され、その方法を知らないため、代わりに props コールバックを使用します。
「スマート」の例AvatarComponent
:
class AvatarComponent extends Component {
expandAvatar () {
this.setState({ loading: true });
sendAjaxRequest(...).then(() => {
this.setState({ loading: false });
});
}
render () {
<div onClick={this.expandAvatar}>
<img src={this.props.username} />
</div>
}
}
「ダム」の例AvatarComponent
:
class AvatarComponent extends Component {
render () {
<div onClick={this.props.onExpandAvatar}>
{this.props.loading && <div className="spinner" />}
<img src={this.props.username} />
</div>
}
}
結局のところ、「ダム」、「ステートレス」、「純粋」は、重複することもあるが、必ずしもそうではない、主に使用事例に応じて、まったく異なる概念であると言えます。