所以我刚刚开始学习redux,并在这个过程中阅读了他们关于“Normalizing State Shape”的文档。
其中一个要点是:
“对单个项目的任何引用都应通过存储项目的ID来完成。”
我已经按照他们的建议设置了我的状态,所以在下面的例子中,每个taskGroup都有一个由它们的id引用的任务选择,每个任务都有一个由它们的id引用的评论选择。
{
"taskGroups": {
"byId": {
"taskGroup1": {
"taskGroupId": "taskGroup1",
"tasks": ["task1", "task2"]
//etc etc
},
"taskGroup2": {
"taskGroupId": "taskGroup2",
"tasks": ["task2", "task3"]
//etc etc
}
//etc etc
},
"allIds": ["taskGroup1", "taskGroup2", "taskGroup3"]
},
"tasks": {
"byId": {
"task1": {
"taskId": "task1",
"description": "......",
"comments": ["comment1", "comment2"]
//etc etc
},
"task2": {
"taskId": "task2",
"description": "......",
"comments": ["comment3", "comment4", "comment5"]
//etc etc
}
//etc etc
},
"allIds": ["task1", "task2", "task3", "task4"]
},
"comments": {
"byId": {
"comment1": {
"id": "comment1",
"comment": "....."
//etc etc
},
"comment2": {
"id": "comment2",
"comment": "....."
//etc etc
}
//etc etc
},
"allIds": ["comment1", "comment2", "comment3", "comment4", "comment5"]
}
}
我理解了这个理论,也看到了像这样构造状态的好处。但在实践中,我很难Map对象中的引用数组,有点不知道应该在哪里做。
什么是最有效的方法,在哪里Map到实际项目的项目ID的引用?
在将引用作为属性传递给子组件之前,我应该在所有引用的父组件Map上执行此操作吗?还是应该将引用作为属性传递给子组件,然后在那里Map它们?
在切换到redux之前,我使用的是useContext,但状态是标准化的。我使用下面的函数来过滤每个taskGroup所需的任务。Thanks to ssube who posted this
export const filterObject = (objToFilter: any, valuesToFind: string) => {
return Object.keys(objToFilter)
.filter((key) => valuesToFind.includes(key))
.reduce((obj, key) => {
return {
...obj,
[key]: objToFilter[key],
};
}, {});
};
然后像这样使用它(然后在我的Tasks组件中重复相同的逻辑来Map注解)
{
Object.values(taskGroupsById)
.sort((a, b) => a.sortOrder - b.sortOrder)
.map((taskGroup) => {
return (
<TaskGroup
key={taskGroup.taskGroupId}
taskGroupTitle={taskGroup.taskGroupTitle}
tasks={filterObject(
tasksById,
taskGroupsById[`${taskGroup.taskGroupId}`].tasks
)}
handleDrawer={handleDrawer}
findTaskStatus={findTaskStatus}
findAssignedToTask={findAssignedToTask}
/>
);
});
}
这可以正常工作,但我不确定它是否违反直觉,因为它是在TaskGroup组件的多个示例中计算的,而不是只计算一次。
有没有更好的方法来实现这一点?我试着在我的切片中创建一个选择器来重新创建它,但似乎无法解决如何Map数组中的多个引用(而不是作为字符串只Map一个引用)。
任何帮助都将不胜感激,即使它只是一个推动正确的方向。谢谢!
2条答案
按热度按时间0yycz8jy1#
在将引用作为属性传递给子组件之前,我应该在所有引用的父组件Map上执行此操作吗?还是应该将引用作为属性传递给子组件,然后在那里Map它们?
我推荐第二种方法。这可以潜在地最小化重新呈现,并且它符合Redux最佳实践连接更多组件以从存储中读取数据。如文档中所解释的:
希望有更多的UI组件订阅Redux存储区,并在更细粒度的级别阅读数据。这通常会带来更好的UI性能,因为当给定的状态发生变化时,需要呈现的组件会更少。
例如,不是只连接
<UserList>
组件并阅读整个用户数组,而是让<UserList>
检索所有用户ID的列表,将列表项呈现为<UserListItem userId={userId}>
,并连接<UserListItem>
并从存储中提取它自己的用户条目。这适用于React-Redux
connect()
API和useSelector()
挂钩。您有一个
taskGroup
对象,如下所示:因此,将任务id数组传递给
<TaskGroup>
组件是很简单的:现在,
TaskGroup
有两种方法可以处理查找任务对象。如果你不需要任何排序,那么你的
TaskGroup
就不需要选择任务对象,它可以简单地遍历id,并将选择推迟到SingleTask
组件,就像docs中的<UserListItem userId={userId}>
示例一样。在
TaskGroup
组件中,您将拥有如下内容:然后每个
SingleTask
负责访问它自己的数据。你可以定义一个选择器函数,尽管这个函数也足够简单,可以内联编写。第一个
如果您需要根据单个任务的属性进行排序或过滤,则需要选择
TaskGroup
中的所有任务对象。创建一个选择器,该选择器接受id数组并返回对象数组。你现有的
filterObject
函数不是特别有效,因为你的状态已经被很好地构造了。你不需要使用任何Array.includes()
语句。你可以简单地通过它们的键来查找对象。在
TaskGroup
组件中,您将拥有:pepwfjgg2#
作为补充回答:Redux Toolkit包含一个
createEntityAdapter
API,它可以自动为规范化数据执行“CRUD”式更新,还可以为“按ID选择一个项”和“将整个项集作为数组选择”生成选择器: