React v15.3.0 から、 PureComponent と呼ばれる新しい基本クラスが PureRenderMixin に組み込まれていることがわかりました。私が理解していることは、フードの下ではこれはshouldComponentUpdate
の中の小道具の浅い比較を採用しているということです。
Reactコンポーネントを定義するための3つの方法があります。
PureComponent
クラスを拡張するコンポーネントComponent
クラスを拡張する通常のコンポーネントしばらく前に、ステートレスコンポーネントを純粋なコンポーネント、あるいはダムコンポーネントと呼ぶこともありました。 「純粋な」という言葉の定義全体がReactで変更されたようです。
私はこれら3つの基本的な違いを理解していますが、私はまだ 何を選択するのか /////// /確実ではないです/ /。また、それぞれのパフォーマンスへの影響とトレードオフは何ですか?
これらは私が明確になることを期待する質問です:
PureComponent
クラスを拡張するのかを選択する必要がありますか?Component
を使用できる場合は、通常のPureComponent
クラスを拡張する必要がありますか?カスタムshouldComponentUpdate
メソッドを使用してReact.PureComponent
またはReact.Component
から拡張すると、パフォーマンスに影響があります。ステートレス機能コンポーネントの使用は「アーキテクチャ上」の選択であり、そのままではパフォーマンス上の利点はありません(まだ)。
簡単に再利用する必要がある単純でプレゼンテーション専用のコンポーネントの場合は、ステートレス機能コンポーネントを使用します。こうすれば、実際のアプリロジックから切り離されていること、テストが簡単であること、予期しない副作用がないことを確認できます。例外は、何らかの理由でたくさんのがある場合、または実際にそれらのレンダリング方法を最適化する必要がある場合です(shouldComponentUpdate
を定義できないため)。ステートレス機能コンポーネント)。
出力が単純な小道具/状態(PureComponentが簡易比較を実行するため、入れ子になったデータ構造がないことを意味する "simple")に依存することがわかっている場合は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.createClass
構文を使って定義されたクラスにアタッチできるPureRenderMixin
を持っていたReact。 mixinは単に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" />
をレンダリングすると、たとえ出力がまったく同じであっても、Reactは毎回render
を呼び出します。ただし、ReactはDOM Diffingを実装しているため、DOMは実際には更新されません。それでも、ドーム拡散を実行することは費用がかかる可能性があるので、このシナリオではそれは無駄になるであろう。
UserAvatar
コンポーネントが代わりにPureComponent
を拡張すると、浅い比較が実行されます。そして小道具とnextPropsは同じなので、render
はまったく呼び出されません。
Reactにおける "pure"の定義に関する注意事項:
一般に、「純粋関数」とは、同じ入力が与えられたときに常に同じ結果に評価される関数です。出力(Reactの場合、render
メソッドによって返されるもの)は履歴や状態に依存せず、副作用(関数の外側の "世界"を変更する操作)もありません。 ).
Reactでは、this.setState
を呼び出さず、this.state
を使用しないコンポーネントを "stateless"と呼ぶ場合、ステートレスコンポーネントは必ずしも上記の定義に従った純粋なコンポーネントとは限りません。
実際、PureComponent
では、ライフサイクルメソッド中に副作用を実行できます。たとえば、componentDidMount
内でajaxリクエストを送信したり、render
内でdivの高さを動的に調整するためのDOM計算を実行することができます。
「ダムコンポーネント」の定義はもっと実用的な意味を持っています(少なくとも私の理解では):ダムコンポーネントはプロップを介して親コンポーネントによって何をすべきかを「教えられ」、使い方を知らないがプロップを使う代わりにコールバック。
"smart" 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>
}
}
結局、「ダム」、「ステートレス」、「ピュア」はまったく異なる概念で、時には重複することもありますが、必ずしもそうとは限りませんが、主にあなたのユースケースによって異なります。
私は反応する以上の天才ではありませんが、私の理解から私たちは以下の状況で各成分を使用することができます
ステートレスコンポーネント - これらはライフサイクルを持たないコンポーネントなので、レンダリングなど親コンポーネントの繰り返し要素のレンダリングに使用する必要があります。情報を表示するだけのテキストリスト。実行するアクションはありません。
純粋なコンポーネント - これらはライフサイクルを持つ項目で、特定の小道具のセットが与えられたときには常に同じ結果を返します。これらのコンポーネントは、複雑な子要素を持たず、それ自体にのみ影響を与える操作を実行するために使用される結果のリストまたは特定のオブジェクトデータを表示するときに使用できます。このようなユーザーカードのリストまたは製品カードのリスト(基本的な製品情報)およびユーザーが実行できる操作のみが、クリックして詳細ページを表示するか、カートに追加することです。
通常のコンポーネントまたは複合コンポーネント - 複合コンポーネントという用語を使用しました。これらは通常ページレベルのコンポーネントであり、多くの子コンポーネントで構成されているためです。あなたはそれが与えられた状態で同じ結果をレンダリングすることを100%確信することができないようにそれ自身のユニークな方法でふるまう。私が通常言ったように、これらはコンテナコンポーネントとして使われるべきです
React.Component
はデフォルトの "normal"コンポーネントです。あなたはそれらをclass
キーワードとextends React.Component
を使って宣言します。ライフサイクルメソッド、イベントハンドラ、そしてあらゆるメソッドを持つクラスとしてそれらを考えてください。
React.PureComponent
は、props
とstate
の浅い比較を行う関数を使ってshouldComponentUpdate()
を実装するReact.Component
です。コンポーネントに変更されたプロップまたはステートネストされたデータがあり、再レンダリングしたい場合は、forceUpdate()
を使用する必要があります。そのため、小道具として渡したり、状態に設定した配列やオブジェクトが変更されたときに、コンポーネントを再レンダリングする必要がある場合、これらは素晴らしいものではありません。
機能コンポーネントは、ライフサイクル機能を持たないものです。それらはおそらくステートレスですが、それらはとても素晴らしくてきれいなので、(React 16.8以降)フックを持っているので、まだステートを持つことができます。だから私はそれらが単なる「クリーンコンポーネント」だと思います。