web-dev-qa-db-ja.com

ストアの状態を小道具として渡しますか、それとも各コンポーネントがグローバルストアにアクセスしますか?

「アプリケーション全体をレンダリングする」と「子コンポーネントに状態を渡す」というステートメントに少し混乱しています。

例1:

AppComponentTodosListComponentのtodosアプリがあります。 AppComponentは、ストアからtodoの配列を取得し、それをプロパティとしてTodosListComponentに渡します。

例2:

私はたくさんの状態を持つ巨大なアプリケーションを持っています。アプリを構築するコンポーネントは50個ほどあります。ストアのすべての状態をAppComponentから50個のコンポーネントすべてに渡しますか?

だから私は疑問に思っています、コンベンションは何ですか?個々のコンポーネントに、関心のあるストアを直接リッスンさせる方が理にかなっています。利点は、個々のコンポーネントのみが再レンダリングされることですが、なぜ「アプリケーション全体が状態の変化に応じて再レンダリングされる」という概念があるのでしょうか。

それぞれの長所と短所は何ですか?一般的な規則は何ですか?

25
christianalfoni

これを処理する方法はいくつかあります。それらはすべて有効であり、独自のトレードオフがあると思います。

すべての状態を取得し、その一部を子供に渡します

これはあなたが具体的に尋ねたテクニックです。このメソッドを使用すると、ストアからのすべてのデータを「状態の大きな袋」に変換するトップレベルコンポーネントで使用できる関数またはメソッドが得られ、このデータの一部を子コンポーネントに選択的に渡します。それらのコンポーネントに独自の子がある場合は、必要に応じてそれらを渡します。

この方法の利点は、一般的にデバッグが容易になることです。状態の一部をストアから取得する方法を変更する必要がある場合は、最上位のコンポーネントで変更するだけで済みます。同じ名前で渡される限り、他のコンポーネントは「正常に機能します。 「」一部のデータが間違っている場合は、その理由を理解するために1か所を調べるだけで済みます。

私が「小道具の爆発」と呼んでいるこの手法の欠点は、lotのプロパティを渡すことになる可能性があることです。このメソッドを中規模のフラックスアプリケーションで使用すると、トップレベルのアプリケーションコンポーネントのスニペットは次のようになります。

<section id="col-left">
  <Filters loading={this.state.loading}
            events={this.state.events}
            playbackRate={this.state.videoPlayback.playbackRate}
            autoPlayAudio={this.state.audioPlayback.autoPlay}
            role={this.state.role} />
</section>

<section id="col-center" className={leftPaneActive ? "" : "inactive"}>
  <SessionVideo videoUuid={this.state.session.recording_uuid}
                lowQualityVideo={this.state.session.low_quality_video_exists}
                playbackRate={this.state.videoPlayback.playbackRate} />
  <section id="transcript">
    <Transcript loading={this.state.loading}
                events={this.state.events}
                currentEvents={this.state.currentEvents}
                selection={this.state.selection}
                users={this.state.session.enrolled_users}
                confirmedHcs={this.state.ui.confirmedHcs}

                currentTime={this.state.videoPlayback.position}
                playing={this.state.videoPlayback.playing} />
  </section>
</section>

特に、最上位のコンポーネントと最終的な子の間に、データを渡す以外は何もしないコンポーネントが多数存在する可能性があり、これらのコンポーネントを階層内の位置により密接に結合します。

全体的に、この手法が提供するデバッグ可能性は気に入っていますが、アプリケーションが大きく複雑になるにつれて、単一のトップレベルコンポーネントだけでこれを行うことは考えられなかったことがわかりました。

すべての状態を取得し、1つのオブジェクトとして渡します

Facebookの開発者の1人がこのテクニックについて言及しました。ここでは、上記と同じように大きな状態のバッグを取得しますが、個々のプロパティではなく、すべて(またはそのサブセクション全体)を渡します。子コンポーネントでReact.PropTypes.shapeを利用することにより、適切なプロパティが確実に渡されるようにすることができます。

利点は、渡すプロパティがはるかに少ないことです。上記の例は次のようになります。

<section id="col-left">
  <Filters state={this.state} />
</section>

<section id="col-center" className={leftPaneActive ? "" : "inactive"}>
  <SessionVideo session={this.state.session}
                playback={this.state.videoPlayback} />
  <section id="transcript">
    <Transcript state={this.state} />
  </section>
</section>

欠点は、状態の形の変化に対処することが少し難しくなることです。トップレベルのコンポーネントを変更するだけでなく、そのデータが使用されているすべての場所を追跡し、コンポーネントがプロパティにアクセスする方法を変更する必要があります。また、shouldComponentUpdateは、実装が少し難しくなる可能性があります。

コンポーネントが独自の状態を取得できるようにする

スペクトルの反対側では、アプリケーション固有の(つまり、再利用できない)子コンポーネントにストアへのアクセスを許可し、それらのownを構築できます。 )ストア変更イベントに基づく状態。このように独自の状態を構築するコンポーネントは、「コントローラービュー」、または最近ではより一般的には「コンテナーコンポーネント」と呼ばれることもあります。

もちろん、利点は、プロパティの受け渡しをまったく処理する必要がないことです(より再利用可能なコンポーネントの変更ハンドラーとプロパティを除く)。

ただし、欠点は、コンポーネントがストアとより高度に結合されていることです。ストアまたはそれらが提供するデータ(またはそれらがデータを提供するインターフェイス)を変更すると、より多くのコンポーネントのコードを再検討する必要が生じる場合があります。

また、コメントで述べたように、これはサーバーのレンダリングを少し難しくする可能性があります。プロパティのみを使用する場合(特にトップレベルのみ)、プロパティをより簡単にクライアントに転送し、同じプロパティでReact)を再初期化できます。ストアが独自のプロパティを決定できるようにすることでデータの場合、コンポーネントがそのデータを取得できるように、何らかの方法でそのデータをストアに挿入する必要があります。

一般的なアプローチであり、私が現在通常使用しているアプローチは、アプリケーション内のすべてのコンポーネントをグローバルアプリケーションの状態の小道具のみに依存させ、(1)それらをラップしてフラックスに直接接続する方が理にかなっているのかどうかを判断することです。コンテナ、または(2)小道具を親コンテナから渡すことができます。


これらのテクニックのいくつかをより実行可能にするために使用できるかもしれない抽象化があります。たとえば、Facebookの開発者は次のように言っています Hacker Newsへのコメント

これですべてのデータがストアに保存されましたが、それを必要とする特定のコンポーネントにどのようにデータを取り込むのでしょうか。私たちは、子供たちに必要なすべてのデータを引き出し、それを小道具に渡す大きなトップレベルのコンポーネントから始めました。これにより、中間コンポーネントに多くの無意味で無関係なコードが作成されます。私たちが決めたのは、いくつかの小さくてより一般的なコンポーネントを除いて、ほとんどの場合、必要なデータを宣言してフェッチするコンポーネントです。ほとんどのデータは非同期でフェッチされてキャッシュされるため、コンポーネントに必要なデータを簡単に宣言し、フェッチと更新のリッスンをライフサイクルメソッド(componentWillMountなど)にフックするミックスインを作成しました。

35
Michelle Tilley

FacebookのJasonBonta 説明 React.js Conf2015の講演での「コンテナ」の概念。

要約すると、コンテナは他のコンポーネントをラップする単なるコンポーネントであり、ストアとの通信などのデータ関連の懸念事項を処理しますが、基になるコンポーネントはビュー(マークアップ/スタイルなど)のみに焦点を当てており、気にしませんデータの出所。

これにより、コンポーネントが作成されます

  • データを別の場所から取得する必要がある場合は、別のコンテナでラップできるため、再利用性が高くなります。

  • 無関係な状態が含まれていないため、shouldComponentUpdateの実装と最適化が容易になり、

  • これにミックスインではなくコンポジションを使用することは、慣用的なミックスインを持たないES6で Reactの将来の可能性が高い と一致します。

[〜#〜] update [〜#〜]2019年3月:調べてください React Hooks 。フックを使用すると、上記と同じ目標を達成できますが、複数のコンポーネントに適用できる再利用可能なコードのチャンクで、ストアとの通信など、データ関連の懸念を抽象化します。 ReactConfトーク React Today and Tomorrow and 90%Cleaner React With Hooks by Dan Abramovは、フックを説明する素晴らしい仕事をしており、それらがミックスインと過去の作曲アプローチ。

4
Raman