如何在redux-orm中将一对多字段作为数组

时间:2017-12-20 19:22:46

标签: javascript redux 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,这使得在传递ti组件时难以处理。

以下是我尝试过的解决方案:

  1. 将返回的每个messages模型的Conversation属性映射到Messages的数组messages.all().toModelArray()。这给了我错误Can't mutate a reverse many-to-one relation,即使我尝试克隆对象。

  2. 创建一个全新的普通旧JavaScript对象并复制所有属性,然后为messages设置正确的值。这很有效,但创建所有这些新对象对于频繁更改状态的应用程序来说似乎是一个巨大的性能损失。

  3. 我应该如何在这里实现目标?

1 个答案:

答案 0 :(得分:3)

你应该能够做类似的事情:

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)时,您需要创建浅层副本。是的,有一个成本,但是超过第一级嵌套的任何内容都使用了引用,因此您需要not cloning the whole thing。使用选择器可以保护您不必在mapStateToProps中完成太多工作(在使用该数据进行第一次渲染之后)。

通过状态管理做出正确的选择很重要,但真正的性能命中可能来自其他来源(不必要的渲染,等待API交互等)。