1. ホーム
  2. javascript

[解決済み] FluxのStoreからコンポーネントはどの入れ子レベルでエンティティを読み込むべきですか?

2023-05-01 07:53:28

質問

Fluxを使用するためにアプリを書き換えているのですが、Storeからのデータ取得に問題があります。たくさんのコンポーネントがあり、それらはたくさんネストしています。そのうちのいくつかは、大きな ( Article )、小さくてシンプルなもの( UserAvatar , UserLink ).

Storesからデータを読み取るのに、コンポーネント階層のどこで読み取ればいいのか悩んでいます。

私は 2 つの極端なアプローチを試しましたが、どちらもあまり好きではありませんでした。

すべてのエンティティ コンポーネントが自分自身のデータを読み取る

Storeから何らかのデータを必要とする各コンポーネントは、エンティティIDだけを受け取り、自分自身でエンティティを取得します。

例えば Article が渡される articleId , UserAvatarUserLink が渡される userId .

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

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>
    )
  }
});

この方法の欠点

  • 100 個ものコンポーネントが Stores にサブスクライブしている可能性があるのは不満です。
  • それは どのようにデータが更新され、どのような順序で更新されるかを追跡するのは難しい。 というのも、各コンポーネントが独立してデータを取得するからです。
  • たとえ はすでに が状態であっても、その ID を子へ渡すことを余儀なくされ、子はそれを再び取得します (さもなければ整合性が失われます)。

すべてのデータはトップレベルで一度読み込まれ、コンポーネントに渡される

バグを追跡するのに疲れたとき、私はすべてのデータ取得をトップレベルに置くことを試みました。しかし、いくつかのエンティティにはいくつかのレベルのネストがあるため、これは不可能であることがわかりました。

たとえば

  • A Category には UserAvatar を含む。
  • について Article は、複数の Category s.

したがって、もし私がStoreからすべてのデータを Article のレベルでStoreからすべてのデータを取得したい場合、私は必要でしょう。

  • から記事を取得する。 ArticleStore ;
  • 全ての記事のカテゴリを CategoryStore ;
  • 各カテゴリの投稿者を個別に UserStore ;
  • どうにかして、すべてのデータをコンポーネントに渡します。

さらに不満なのは、深くネストされたエンティティが必要なときはいつでも、さらにそれを下に渡すために、ネストの各レベルにコードを追加する必要があったことです。

まとめ

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

私の目標

  • 店舗は、非常識な数の購読者を持つべきではありません。それは、それぞれの UserLink をリッスンするために UserStore をリッスンします。

  • 親コンポーネントがストアから何らかのオブジェクトを取得した場合(例えば user のように)、ネストされたコンポーネントが再びそれを取得する必要がないようにしたいです。私はそれをプロップス経由で渡すことができるはずです。

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

どのように解決するのですか?

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

私たちの例では Article には article プロップはオブジェクトである (おそらく ArticleList または ArticlePage ).

なぜなら Article もレンダリングしたいからです。 UserLinkUserAvatar を登録すると、記事の著者のために UserStore を購読し author: UserStore.get(article.authorId) をその状態のままにしておきます。その後、レンダリングされる UserLinkUserAvatar を、この this.state.author . もし、このユーザーをさらに引き継ぎたい場合は、引き継ぐことができます。子コンポーネントがこのユーザーを再び取得する必要はありません。

繰り返しになりますが

  • どのコンポーネントもIDをpropとして受け取ることはなく、すべてのコンポーネントはそれぞれのオブジェクトを受け取ります。
  • 子コンポーネントがエンティティを必要とする場合、親の責任でそれを取得し、propとして渡します。

これは私の問題をうまく解決してくれました。この方法を使うためにコード例を書き直しました。

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>
    )
  }
});

これにより、最内部のコンポーネントは愚かなままですが、トップレベルのコンポーネントを地獄のように複雑にすることを強制されません。