2

我有一个 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
        />
   }
}

在实现上,我陷入了制作选择器的困境,找不到这种情况的示例 for redux-orm v0.19,而且从概念上讲,我仍然不确定哪种方式是最好的选择。非常感谢所有帮助!

4

1 回答 1

4

更新(2019-09-30):

从 v0.14 开始,有一种更简单的方法来创建通用选择器。首先,teams选择器可以这样写:

const teams = createSelector(orm.Team);

与您对 a 所做的类似,QuerySet您可以创建一个选择器来检索球队的球员作为参考数组:

const teamPlayers = createSelector(orm.Team.players);

使用 react-redux,你可以简单地将一个团队传递给一个 React 组件,然后在商店更新时获取所有相关数据。

function TeamList(props) {
  const teams = createSelector(state => teams(state));
  return (
    <ul>
      {teams.map((team, i) => (
        <Team key={team.id} team={team} />)
      )}
    </ul>
  );
}

function Team({ team }) {
  const players = useSelector(state => teamPlayers(state, team.id));
  return (
    <div>
      {team.name}: {players.map(player => player.name).join(', ')}
    </div>
  );
}

您也可以teamPlayers在父组件中使用选择器,但根据我的经验,如果组件自己检索其依赖项,通常会更好地实现可重用性。team.id因此,有时传递给Team而不是整个team参考可能会更好。没有在这里做,因为它看起来没用。

请注意,teams(state)按插入顺序返回所有团队。不要尝试像在迭代时那样对它进行索引,teams(state)[team.id]因为team.id它通常不匹配团队在数组中的索引。要获取单个团队,请传递其主键,例如teams(state, team.id). 有关更多详细信息,请查看我们的新文档


简短的回答是:没有正确的方法。以前在这里被问过。

但是,最简单的方法是直接附加播放器引用,如下所示:

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

这将允许您直接访问您的玩家作为您的团队对象的数组属性,并且您不需要添加任何其他内容。但是,如果您想在不访问 player 数组的情况下重用选择器,它可能会产生开销。在您看来,您可以将整个团队引用或其中的一部分作为道具传递:

<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 提供一种更简单的方法来创建或访问每个人都需要的这些类型的选择器。现在它非常灵活,但还不是很方便。

于 2018-07-13T10:37:38.493 回答