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