我使用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组件时很难处理。
这是我尝试过的解决方案:
messages
模型的Conversation
属性返回到与Messages
的messages.all().toModelArray()
阵列。这给了我错误Can't mutate a reverse many-to-one relation
,即使我尝试克隆对象。messages
设置正确的值。这很有效,但创建所有这些新对象对于频繁更改状态的应用程序来说似乎是一个巨大的性能损失。我该如何在这里实现我的目标?
你应该能够做类似的事情:
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交互等)。