Селекторы с React + 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 Component, который отображает список всех команд со всеми игроками. Поэтому первым делом я написал селектор:

// /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 ответ

Короткий ответ: нет правильного пути. Это было задано здесь раньше.

Тем не менее, самый простой способ сделать это - добавить ссылки на игрока прямо так:

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

Это позволит вам получить доступ к игрокам напрямую как к свойству массива объектов вашей команды, и вам не нужно будет ничего добавлять. Это имеет обратную сторону возможных накладных расходов, если вы хотите повторно использовать селектор, не обращаясь к массиву игроков, хотя. По вашему мнению, вы можете передать всю команду или ее часть в виде реквизита:

<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 сможет предоставить более простой способ создания или доступа к таким селекторам, которые нужны всем. Сейчас он очень гибкий, но не совсем удобный.

Другие вопросы по тегам