89

Flux を使用するようにアプリを書き直していますが、ストアからのデータの取得に問題があります。たくさんのコンポーネントがあり、それらはたくさん入れ子になっています。大きいもの ( Article) もあれば、小さくてシンプルなもの ( UserAvatarUserLink) もあります。

コンポーネント階層のどこでストアからデータを読み取る必要があるかで苦労しています。
私は 2 つの極端なアプローチを試みましたが、どちらもあまり好きではありませんでした。

すべてのエンティティ コンポーネントが独自のデータを読み取ります

Store からのデータを必要とする各コンポーネントは、エンティティ ID だけを受け取り、独自にエンティティを取得します。
たとえば、Articleis passed articleIdUserAvatarおよびUserLinkare passeduserIdです。

このアプローチには、いくつかの重大な欠点があります (コード サンプルで説明します)。

var Article = React.createClass({
  mixins: [createStoreMixin(ArticleStore)],

  propTypes: {
    articleId: PropTypes.number.isRequired
  },

  getStateFromStores() {
    return {
      article: ArticleStore.get(this.props.articleId);
    }
  },

  render() {
    var article = this.state.article,
        userId = article.userId;

    return (
      <div>
        <UserLink userId={userId}>
          <UserAvatar userId={userId} />
        </UserLink>

        <h1>{article.title}</h1>
        <p>{article.text}</p>

        <p>Read more by <UserLink userId={userId} />.</p>
      </div>
    )
  }
});

var UserAvatar = React.createClass({
  mixins: [createStoreMixin(UserStore)],

  propTypes: {
    userId: PropTypes.number.isRequired
  },

  getStateFromStores() {
    return {
      user: UserStore.get(this.props.userId);
    }
  },

  render() {
    var user = this.state.user;

    return (
      <img src={user.thumbnailUrl} />
    )
  }
});

var UserLink = React.createClass({
  mixins: [createStoreMixin(UserStore)],

  propTypes: {
    userId: PropTypes.number.isRequired
  },

  getStateFromStores() {
    return {
      user: UserStore.get(this.props.userId);
    }
  },

  render() {
    var user = this.state.user;

    return (
      <Link to='user' params={{ userId: this.props.userId }}>
        {this.props.children || user.name}
      </Link>
    )
  }
});

このアプローチの欠点:

  • 何百ものコンポーネントがストアにサブスクライブする可能性があるのはイライラします。
  • 各コンポーネントが個別にデータを取得するため、データがどのように更新され、どのような順序で更新されるかを追跡することは困難です。
  • 状態のエンティティが既にある場合でも、その ID を子に渡す必要があり、子はエンティティを再度取得します (または、一貫性を破ります)。

すべてのデータは最上位で一度読み取られ、コンポーネントに渡されます

バグの追跡にうんざりしていたとき、すべてのデータ取得を最上位にしようとしました。ただし、エンティティによっては複数レベルのネストがあるため、これは不可能であることが判明しました。

例えば:

  • Aには、そのカテゴリに貢献する s 人の人がCategory含まれます。UserAvatar
  • AnArticleには複数Categoryの が含まれる場合があります。

したがって、ストアから のレベルですべてのデータを取得する場合はArticle、次のようにする必要があります。

  • から記事を取得しArticleStoreます。
  • からすべての記事のカテゴリを取得しCategoryStoreます。
  • から各カテゴリの貢献者を個別に取得しますUserStore
  • どういうわけか、そのすべてのデータをコンポーネントに渡します。

さらに苛立たしいことに、深くネストされたエンティティが必要な場合は常に、ネストの各レベルにコードを追加して、それをさらに渡す必要があります。

まとめ

どちらのアプローチにも欠陥があるようです。この問題を最もエレガントに解決するにはどうすればよいですか?

私の目的:

  • ストアには、非常識な数の加入者がいるべきではありません。親コンポーネントがすでにそれを行っている場合、それぞれUserLinkがリッスンするのはばかげています。UserStore

  • 親コンポーネントがストアから何らかのオブジェクトを取得した場合 (例: user)、ネストされたコンポーネントがそれを再度取得する必要はありません。私は小道具を介してそれを渡すことができるはずです。

  • リレーションシップの追加や削除が複雑になるため、すべてのエンティティ (リレーションシップを含む) をトップ レベルでフェッチする必要はありません。ネストされたエンティティが新しい関係を取得するたびに、すべてのネスト レベルで新しい props を導入したくありません (たとえば、カテゴリが を取得しますcurator)。

4

4 に答える 4

37

私がたどり着いたアプローチは、各コンポーネントがそのデータ (ID ではなく) を小道具として受け取るようにすることです。ネストされたコンポーネントが関連エンティティを必要とする場合、それを取得するのは親コンポーネント次第です。

この例では、オブジェクトでArticleある prop が必要です (おそらくまたはによって取得されます)。articleArticleListArticlePage

記事の作成者に対してもレンダリングする必要があるため、サブスクライブしてArticleその状態を維持します。次に、これを使用してレンダリングします。彼らがそれをさらに伝えたいのであれば、そうすることができます。子コンポーネントがこのユーザーを再度取得する必要はありません。UserLinkUserAvatarUserStoreauthor: UserStore.get(article.authorId)UserLinkUserAvatarthis.state.author

繰り返します:

  • プロパティとして ID を受け取るコンポーネントはありません。すべてのコンポーネントはそれぞれのオブジェクトを受け取ります。
  • 子コンポーネントがエンティティを必要とする場合、それを取得して小道具として渡すのは親の責任です。

これは私の問題をかなりうまく解決します。このアプローチを使用するように書き直されたコード例:

var Article = React.createClass({
  mixins: [createStoreMixin(UserStore)],

  propTypes: {
    article: PropTypes.object.isRequired
  },

  getStateFromStores() {
    return {
      author: UserStore.get(this.props.article.authorId);
    }
  },

  render() {
    var article = this.props.article,
        author = this.state.author;

    return (
      <div>
        <UserLink user={author}>
          <UserAvatar user={author} />
        </UserLink>

        <h1>{article.title}</h1>
        <p>{article.text}</p>

        <p>Read more by <UserLink user={author} />.</p>
      </div>
    )
  }
});

var UserAvatar = React.createClass({
  propTypes: {
    user: PropTypes.object.isRequired
  },

  render() {
    var user = this.props.user;

    return (
      <img src={user.thumbnailUrl} />
    )
  }
});

var UserLink = React.createClass({
  propTypes: {
    user: PropTypes.object.isRequired
  },

  render() {
    var user = this.props.user;

    return (
      <Link to='user' params={{ userId: this.props.user.id }}>
        {this.props.children || user.name}
      </Link>
    )
  }
});

これにより、最も内側のコンポーネントを愚かに保つことができますが、トップレベルのコンポーネントを複雑にする必要はありません。

于 2014-09-06T14:16:10.960 に答える
0

あなたの 2 つの哲学で説明されている問題は、すべての単一ページ アプリケーションに共通しています。

https://www.youtube.com/watch?v=IrgHurBjQbgと Relay ( https://facebook.github.io/relay ) は、あなたが説明するトレードオフを克服するために Facebook によって開発されました。

Relayのアプローチは非常にデータ中心です。これは、「サーバーへの 1 回のクエリで、このビューの各コンポーネントに必要なデータだけを取得するにはどうすればよいですか?」という質問に対する回答です。同時に、Relay は、コンポーネントが複数のビューで使用されている場合、コード間の結合がほとんどないことを確認します。

Relay がオプションでない場合は、「すべてのエンティティ コンポーネントが独自のデータを読み取る」という方法が、あなたが説明した状況に対するより良いアプローチと思われます。Flux の誤解は、ストアとは何かということだと思います。ストアの概念は、モデルまたはオブジェクトのコレクションが保持される場所ではありません。ストアは、ビューがレンダリングされる前にアプリケーションがデータを配置する一時的な場所です。それらが存在する本当の理由は、異なるストアに入るデータ間の依存関係の問題を解決することです。

Flux が指定していないのは、ストアがモデルの概念とオブジェクトのコレクション (バックボーンのようなもの) にどのように関係しているかです。その意味で、ユーザーがブラウザを開いたままにしている間ずっとフラッシュされない特定のタイプのオブジェクトのコレクションを配置する場所としてフラックスストアを実際に作成している人もいますが、私が理解しているように、フラックスはストアではありませんであるはずです。

解決策は、ビューをレンダリングするために必要なエンティティ (および場合によってはそれ以上) が格納され、更新されている別のレイヤーを用意することです。モデルとコレクションを抽象化するこのレイヤーの場合、サブコンポーネントが独自のデータを取得するために再度クエリを実行する必要がある場合でも問題ありません。

于 2015-10-16T22:03:56.050 に答える