React機能のステートレスコンポーネント、PureComponent、Component; 違いは何ですか、いつ何を使うべきですか?


188

ことを知っているに来たv15.3.0反応し、我々は新しいベース・クラスと呼ばれる持ってPureComponentに拡張するPureRenderMixinビルトインを。私が理解しているのは、内部では小道具の浅い比較を採用しているということshouldComponentUpdateです。

これで、Reactコンポーネントを定義する3つの方法があります。

  1. クラスを拡張しない機能的なステートレスコンポーネント
  2. PureComponentクラスを拡張するコンポーネント
  3. Componentクラスを拡張する通常のコンポーネント

昔、ステートレスコンポーネントをPure Components、またはDumb Componentsと呼んでいました。「純粋」という言葉の定義全体がReactで変更されたようです。

これら3つの基本的な違いは理解していますが、いつ何を選択するかはまだわかりません。また、それぞれのパフォーマンスへの影響とトレードオフは何ですか?


更新

これらは私が明らかにすることを期待する質問です:

  • 単純なコンポーネントを機能的(単純化するため)に定義するか、PureComponentクラスを拡張する(パフォーマンスのために)ように定義する必要がありますか?
  • 私が失ったシンプルさと実際のトレードオフで得られるパフォーマンスの向上はありますか?
  • Component常にPureComponentパフォーマンスを向上させるために使用できる場合、通常のクラスを拡張する必要がありますか?

回答:


315

コンポーネントの目的/サイズ/小道具/動作に基づいて、これら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" />同じプロップでレンダリングした場合、Reactはrender場合、出力がまったく同じであっても毎回れます。ただし、Reactはdom diffingを実装しているため、DOMは実際には更新されません。それでも、dom diffの実行はコストがかかる可能性があるため、このシナリオでは無駄になります。

場合UserAvatarコンポーネントが延びるPureComponent代わりに、比較浅いが行われます。また、propsとnextPropsは同じであるため、まったくrender呼び出されません。

Reactの「純粋」の定義に関する注記:

一般に、「純粋な関数」は、同じ入力が与えられた場合に常に同じ結果に評価される関数です。出力(Reactの場合、それはrenderメソッドによって返されます)は、履歴/状態に依存せず、副作用(関数の外部の「世界」を変更する操作)もありません。

Reactでは、決して呼び出さthis.setStateず、を使用しないコンポーネントを「ステートレス」と呼ぶ場合、ステートレスコンポーネントは必ずしも上記の定義に従って純粋なコンポーネントであるとは限りませんthis.state

実際、では、PureComponentライフサイクルメソッド中に副作用を実行することができます。たとえば、ajaxリクエストを内部に送信したり、componentDidMountDOM計算を実行して内のdivの高さを動的に調整したりできますrender

「ダムコンポーネント」の定義には、(少なくとも私の理解では)より「実用的な」意味があります。ダムコンポーネントは、親コンポーネントがプロップを介して何をすべきかを「伝えられ」、方法を知らないがプロップを使用します代わりにコールバック。

「スマート」の例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>
    }
}

最後に、「ダム」、「ステートレス」、および「ピュア」はまったく異なる概念であり、ほとんどの場合、ユースケースによっては重複する場合がありますが、必ずしもそうとは限りません。


1
私はあなたの答えとあなたが共有した知識に本当に感謝しています。しかし、私の本当の質問は、いつ何を選択すべきかということです。。回答で述べたのと同じ例について、どのように定義すればよいですか?それは機能的なステートレスコンポーネント(そうであればなぜですか?)、またはPureComponent(なぜですか?)を拡張するか、またはComponentクラス(これもなぜですか?)を拡張する必要があります。コンポーネントの目的/サイズ/小道具/動作に基づいて、これら3つからどのように選択しますか?
Yadhu Kiran 2016

1
問題ない。機能的なステートレスコンポーネントの場合、長所/短所のリストがあり、これが適切かどうかを判断するために検討できます。それで最初のポイントはわかりますか?選択の質問についてもう少し説明します。
fabio.sussetto 2016年

2
機能コンポーネントは、たとえまったく使用propsしていなくても、親コンポーネントが更新されると常に再レンダリングされます。
AlexM 2017

1
これは、私がかなり前に読んだ最も包括的な回答の1つです。すごい仕事。最初の文についての1つのコメント:を拡張するときPureComponentは、実装しないでくださいshouldComponentUpdate()。これを実際に行うと警告が表示されます。
jjramos 2018年

1
実際のパフォーマンスを向上させるにはPureComponent、オブジェクト/配列のプロパティがネストされているコンポーネントに使用してみてください。もちろん、あなたは何が起こっているのかを認識する必要があります。私が正しく理解していればあなたは(警告をやってからあなたを防ぐための試みを反応する)を直接小道具/状態を変異または外部ライブラリを経由していない場合、あなたは使用して問題ないはずPureComponentはなく、Componentほとんどどこでも...除いて実際にはそれを使用しない方が高速になる可能性のある非常に単純なコンポーネント-news.ycombinator.com/item?id=14418576を
Matt Browne

28

私は反応の天才ではありませんが、私の理解から、以下の状況で各コンポーネントを使用できます

  1. ステートレスコンポーネント- これらはライフサイクルを持たないコンポーネントなので、これらのコンポーネントは、情報を表示するだけで実行するアクションがないテキストリストのレンダリングなど、親コンポーネントの繰り返し要素のレンダリングで使用する必要があります。

  2. 純粋なコンポーネント-これらはライフサイクルを持つアイテムであり、特定の小道具のセットが与えられた場合、常に同じ結果を返します。これらのコンポーネントは、結果のリストまたは複雑な子要素を持たない特定のオブジェクトデータを表示するときに使用でき、それ自体にのみ影響する操作を実行するために使用されます。このようなユーザーカードのリストまたは製品カードのリスト(基本的な製品情報)とユーザーが実行できるアクションのみをクリックして、詳細ページを表示するか、カートに追加します。

  3. 通常のコンポーネントまたは複雑なコンポーネント-複雑なコンポーネントという用語を使用しました。これらは通常、ページレベルのコンポーネントであり、多くの子コンポーネントで構成されており、それぞれの子は独自の方法で動作できるため、100%確実ではないからです。指定された状態で同じ結果をレンダリングします。私が通常言ったように、これらはコンテナコンポーネントとして使用されるべきです


1
このアプローチは機能しますが、大きなパフォーマンスの向上を見逃している可能性があります。PureComponentルートレベルのコンポーネントと階層の最上位近くのコンポーネントを使用すると、通常、パフォーマンスが最大に向上します。もちろん、純粋なコンポーネントが正しく機能するためには、propsとstateを直接変更する必要はありませんが、オブジェクトを直接変更することは、とにかくReactのアンチパターンです。
マットブラウン

5
  • React.Componentデフォルトの「通常の」コンポーネントです。classキーワードとを使用して宣言しますextends React.Component。それらはクラスであり、ライフサイクルメソッド、イベントハンドラ、その他のメソッドを備えていると考えてください。

  • React.PureComponentReact.Componentその実装shouldComponentUpdate()のの浅い比較し機能を持つpropsとしstateforceUpdate()コンポーネントに変更されたプロップまたは状態のネストされたデータがあり、再レンダリングしたい場合に使用する必要があります。そのため、小道具として渡すか、状態に設定した配列またはオブジェクトが変化したときに、コンポーネントを再レンダリングする必要がある場合、それらは適切ではありません。

  • 機能コンポーネントとは、ライフサイクル機能を持​​たないコンポーネントです。それらはおそらくステートレスですが、とてもきれいでクリーンなのでフック(React 16.8以降)があるので、まだ状態を保持できます。したがって、これらは単なる「クリーンなコンポーネント」だと思います。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.