Как получить поле "один ко многим" в виде массива в redux-orm
У меня есть следующие модели для приложения чата, использующего redux-orm
, каждый Conversation
содержит много Messages
, но одно сообщение может принадлежать только одному Conversation
:
export class Message extends Model {
static modelName = 'Message';
static fields = {
id: attr(),
text: attr(),
created: attr(),
from: fk('User'),
conversation: fk('Conversation', 'messages')
};
}
export class Conversation extends Model {
static modelName = 'Conversation';
static fields = {
id: attr(),
created: attr(),
};
}
Я использую следующий селектор, чтобы получить список разговоров с соответствующими сообщениями.
export const getConversations = createSelector(
getOrm,
createOrmSelector(orm, session => {
return session.Conversation
.all()
.toModelArray()
})
);
Эта проблема? messages
собственность каждого Conversation
Экземпляр QuerySet
не Array
, что затрудняет работу при передаче компонентов.
Вот решения, которые я попробовал:
Картирование
messages
собственность каждогоConversation
модель вернулась в массивMessages
сmessages.all().toModelArray()
, Это дало мне ошибкуCan't mutate a reverse many-to-one relation
даже когда я пытался клонировать объект.Создание совершенно нового простого старого объекта JavaScript и копирование всех свойств, а затем установка правильного значения для
messages
, Это сработало, но создание всех этих новых объектов кажется огромным скачком производительности приложения с частыми изменениями состояния.
Как мне достичь своей цели здесь?
1 ответ
Вы должны быть в состоянии сделать что-то похожее на:
return session.Conversation.all().toModelArray()
.map(c => ({
...c.ref,
messages: c.messages.toRefArray()
}))
в вашем селекторе. Если это не работает, вам, возможно, придется включить больше деталей. Вы не получаете отношения бесплатно с начальным toModelArray
вам нужно "запросить" их, чтобы использовать их в селекторе.
Обычно я бы не просто выкидывал все содержимое хранилища для этих сущностей, как это, я бы уточнил, что в действительности требует компонент:
import { pick } from 'lodash/fp'
// ...
const refineConversation = c => ({
...pick([ 'id', 'updatedAt' ], c),
messages: c.messages.toRefArray().map(refineMessages)
})
const refineMessages = m => ({
...pick([ 'id', 'author', 'text', 'updatedAt' ], m)
})
// ...
return session.Conversation
.all()
.toModelArray()
.map(refineConversation)
Хотя может показаться заманчивым просто сгенерировать ссылку на объект из хранилища в вашем компоненте, в этом объекте есть куча вещей, которые ваш компонент, вероятно, не должен отображать. Если что-то из этого изменится, компонент должен выполнить повторную визуализацию, и ваш селектор не сможет использовать свои записанные данные.
Помните, что когда вы используете объект распространения (или Object.assign
Вы создаете мелкие копии. Да, это цена, но все, что выходит за рамки первого уровня вложенности, использует ссылку, поэтому вы не клонируете все это. Использование селекторов должно защитить вас от необходимости делать слишком много работы в mapStateToProps
(после первого рендера с этими данными).
Важно сделать правильный выбор с помощью управления состоянием, но реальные потери производительности, вероятно, будут приходить из других источников (ненужные рендеры, ожидание взаимодействия API и т. Д.).