具有React + Redux-ORM的选择器。将数据传递到子组件

时间:2018-07-12 12:28:43

标签: reactjs react-redux redux-orm

我有一个React应用程序,我正在尝试使用Reux-ORM,并且在选择器方面苦苦挣扎。我有一个带有标准化数据的简单应用

// /models/team.js
import { Model, many, attr } from 'redux-orm';
import Player from './player';

class Team extends Model {
  static reducer(action, Team, session) {
    [...]
  }
}
Team.modelName = "Team";
Team.fields = {
  id: attr(),
  players: many('Player;),
}

// /models/player.js
import { Model, attr } from 'redux-orm';

class Player extends Model {
  static reducer(action, Player, session) {
    [...]
  }
}
Player.modelName = "Player";
Player.fields = {
  id: attr(),
}

我将它们注册到ORM如下:

// /selectors/selector.js
import { ORM } from 'redux-orm';

import Team from '../models/team';
import Player from '../models/player';

const orm = new ORM();
orm.register(Team, Player);

export default orm;

到目前为止,一切都很好。现在,我想拥有一个React组件,该组件可以呈现所有玩家所有团队的列表。所以我要做的第一件事是编写一个选择器:

// /selectors/selector.js
import { createSelector } from "redux-orm";
import orm from "../models/index";

export const teams = createSelector(
  orm,
  state => state.orm,
  session => {
    return session.Team.all().toRefArray();
  }
);

这给出了球队名单,但还没有相关的球员。因此,在我的组件中,如果添加函数teams

,现在可以在道具中使用const mapStateToProps = state => ({teams: teams(state)});

但是现在我的问题是,获得团队特定球员的最佳方法是什么?是否将它们添加到选择器中返回的团队中?还是我写一个单独的选择器?我会直接将玩家传递给组件,还是在团队组件中创建一个单独的组件?

class Teams extends React.Components {
   render() {
    return this.props.teams.map(team => {
      /* Option 1: */
      const teamPlayer = team.players // returned by initial selector
      /* Option 2: */
      const teamPlayers = [some call to selector based on 'team'];
      /* Option 3: */
      // Don't pass on the players but the id instead
      return (
        <Team
          /* Option 1 & 2: */
          players= {teamPlayers}
          /* Option 3: */
          teamId = {team.id} // and within the Team component make a selector call
        />
   }
}

实际上,我陷入了做出选择器的困境,无法为redux-orm v0.19的这种情况找到示例,从概念上讲,我仍然不确定哪种方法最好。感谢所有帮助!

1 个答案:

答案 0 :(得分:1)

简单的答案是:没有正确的方法。之前here已经有人问过。

但是,最简单的方法是像这样直接添加播放器引用:

export const teams = createSelector(
  orm,
  state => state.orm,
  ({ Team }) => {
    return Team.all().toModelArray().map(team => ({
      ...team.ref,
      players: team.players.toRefArray(),
    }));
  }
);

这将使您可以直接作为团队对象的数组属性访问玩家,而无需添加其他任何内容。但是,如果您想在不访问players数组的情况下重用选择器,则可能会有开销。在您看来,您可以将整个团队参考或部分参考作为支持:

<Team
  // either entire team object
  team={team}

  // or each field individually
  teamId={team.id}
  players={team.players}
/>

或者,您可以为每个需要查询的关系创建一个单独的选择器:

export const teamPlayers = createSelector(
  orm,
  state => state.orm,
  ({ Team }) => {
    return Team.all().toModelArray().reduce(map, team => ({
      ...map,
      [team.id]: team.players.toRefArray(),
    }));
  }
);

<Team
  // either entire team object
  team={team}
  teamPlayers={teamPlayers}

  // or each field individually
  teamId={team.id}
  players={teamPlayers[team.id]}
/>

这是一个很明显的问题,每次您要使用关系访问器时,都需要添加选择器或至少增加最小的样板。

在组件本身内部调用选择器不是实现此目的的方法,因为在商店更新时将仅调用mapStateToProps。除非您愿意并且有充分的理由再次在子组件中connect,否则我强烈建议您不要使用第三个选项。

如果您觉得所有这些都是一团糟,那么您肯定并不孤单-我已经思考了一段时间了。我们也许可以让Redux-ORM提供一种更轻松的方法来创建或访问每个人都需要的选择器。现在,它非常灵活,但还不太方便。