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

How to get a one-to-many field as an array in 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()
  })
);

问题?每个Conversation实例的messages属性是一个QuerySet,而不是Array,这使得传递ti组件时很难处理。

这是我尝试过的解决方案:

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

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

我应该如何实现我的目标?

您应该能够做类似于以下的事情:

return session.Conversation.all().toModelArray()
  .map(c => ({
    ...c.ref,
    messages: c.messages.toRefArray()
  }))

在您的选择器中。如果这不起作用,您可能需要包含更多详细信息。您不会免费获得与初始 toModelArray 的关系,您确实需要 'query' 它们才能在选择器中使用它们。

通常我不会像这样转储这些实体的整个存储内容,我会将它们细化为组件实际需要的内容:

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 交互等)。