在“设计状态形状”一章中,文档建议将您的状态保留在由ID设置关键帧的对象中:
将对象中的每个实体以ID作为键存储,并使用ID从其他实体或列表中引用它。
他们接着说
将应用的状态视为数据库。
我正在处理过滤器列表的状态形状,其中一些过滤器将被打开(它们显示在弹出窗口中),或者具有选定的选项。当我读到“把应用程序的状态想象成一个数据库”时,我想把它们想象成一个JSON响应,因为它将从API(本身由数据库支持)返回。
所以我认为它是
[{
id: '1',
name: 'View',
open: false,
options: ['10', '11', '12', '13'],
selectedOption: ['10'],
parent: null,
},
{
id: '10',
name: 'Time & Fees',
open: false,
options: ['20', '21', '22', '23', '24'],
selectedOption: null,
parent: '1',
}]
然而,这些文件建议的格式更像
{
1: {
name: 'View',
open: false,
options: ['10', '11', '12', '13'],
selectedOption: ['10'],
parent: null,
},
10: {
name: 'Time & Fees',
open: false,
options: ['20', '21', '22', '23', '24'],
selectedOpthtml" target="_blank">ion: null,
parent: '1',
}
}
理论上,只要数据是可序列化的(在“状态”标题下),它就不重要了。
所以我很高兴地采用了对象数组的方法,直到我编写了我的还原器。
通过id键控对象的方法(以及扩展语法的自由使用),OPEN\u过滤器
是reducer的一部分
switch (action.type) {
case OPEN_FILTER: {
return { ...state, { ...state[action.id], open: true } }
}
而对于对象数组方法,它更冗长(并且依赖于辅助函数)
switch (action.type) {
case OPEN_FILTER: {
// relies on getFilterById helper function
const filter = getFilterById(state, action.id);
const index = state.indexOf(filter);
return state
.slice(0, index)
.concat([{ ...filter, open: true }])
.concat(state.slice(index + 1));
}
...
1)减速器的简单性是否是采用对象键控方式的动机?这种状态还有其他优势吗?
和
2) 似乎用id键控对象的方法使得处理API的标准JSON输入/输出变得更加困难。(这就是我首先使用对象数组的原因。)那么,如果使用这种方法,是否只需使用函数在JSON格式和状态形状格式之间来回转换?那看起来很笨重。(不过,如果你提倡这种方法,你的部分推理是不是比上面的对象数组更简单?)
和
3) 我知道Dan Abramov将redux设计为理论上不可知的状态数据结构(正如“按照惯例,顶级状态是一个对象或一些其他键值集合,如地图,但技术上它可以是任何类型,”emphasis mine指出)。但是考虑到上述情况,是否只是“建议”将其保留为ID键控的对象,或者使用一组对象使其成为我应该中止该计划并尝试坚持使用ID键控的对象时,是否会遇到其他无法预见的痛点?
1)减速器的简单性是否是采用对象键控方式的动机?这种状态还有其他优势吗?
您希望将实体保留在以ID作为键存储的对象中(也称为规范化)的主要原因是,处理深度嵌套的对象(这是您通常在更复杂的应用程序中从REST API中获得的)非常麻烦组件和您的减速器。
用您当前的示例来说明规范化状态的好处有点困难(因为您没有深度嵌套的结构)。但假设选项(在您的示例中)也有一个标题,并且是由系统中的用户创建的。这将使响应看起来像这样:
[{
id: 1,
name: 'View',
open: false,
options: [
{
id: 10,
title: 'Option 10',
created_by: {
id: 1,
username: 'thierry'
}
},
{
id: 11,
title: 'Option 11',
created_by: {
id: 2,
username: 'dennis'
}
},
...
],
selectedOption: ['10'],
parent: null,
},
...
]
现在让我们假设您想要创建一个组件,该组件显示已创建选项的所有用户的列表。要做到这一点,您必须首先请求所有项目,然后迭代它们的每个选项,最后获得created_by.username。
更好的解决方案是将响应标准化为:
results: [1],
entities: {
filterItems: {
1: {
id: 1,
name: 'View',
open: false,
options: [10, 11],
selectedOption: [10],
parent: null
}
},
options: {
10: {
id: 10,
title: 'Option 10',
created_by: 1
},
11: {
id: 11,
title: 'Option 11',
created_by: 2
}
},
optionCreators: {
1: {
id: 1,
username: 'thierry',
},
2: {
id: 2,
username: 'dennis'
}
}
}
有了这个结构,列出所有已经创建选项的用户会更容易,也更高效(我们在entities.option创建者中隔离了他们,所以我们只需要循环浏览那个列表)。
显示那些为ID为1的筛选项创建选项的用户名也很简单:
entities
.filterItems[1].options
.map(id => entities.options[id])
.map(option => entities.optionCreators[option.created_by].username)
2) 似乎用id键控对象的方法使得处理API的标准JSON输入/输出变得更加困难。(这就是我首先使用对象数组的原因。)那么,如果使用这种方法,是否只需使用函数在JSON格式和状态形状格式之间来回转换?那看起来很笨重。(不过,如果你提倡这种方法,你的部分推理是不是比上面的对象数组更简单?)
JSON响应可以使用normalizer等进行规范化。
3) 我知道Dan Abramov将redux设计为理论上不可知的状态数据结构(正如“按照惯例,顶级状态是一个对象或一些其他键值集合,如地图,但技术上它可以是任何类型,”emphasis mine指出)。但是考虑到上述情况,是否只是“建议”将其保留为ID键控的对象,或者使用一组对象使其成为我应该中止该计划并尝试坚持使用ID键控的对象时,是否会遇到其他无法预见的痛点?
这可能是对具有大量深度嵌套API响应的更复杂应用程序的建议。不过,在您的特定示例中,这其实并不重要。
将应用的状态视为数据库。
这是关键的想法。
1) 具有唯一id的对象允许您在引用对象时始终使用该id,因此您必须在操作和还原器之间传递最小数量的数据。它比使用array.find(…)更有效。如果使用数组方法,则必须传递整个对象,而且很快就会变得混乱,最终可能会在不同的还原器、动作甚至容器中重新创建对象(您不希望这样)。视图将始终能够获取完整对象,即使其关联的缩减器仅包含ID,因为在映射状态时,您将在某处获取集合(视图获取将其映射到属性的整个状态)。由于我所说的,操作最终拥有最少的参数,减少了最少的信息量,尝试一下,两种方法都尝试一下,如果集合确实有ID,您将看到使用ID的体系结构最终更具可伸缩性和干净性。
2)与API的连接不应该影响存储和减速器的体系结构,这就是为什么你有操作来保持关注点的分离。只需将转换逻辑放入和移出可重用模块中的API,将该模块导入使用该API的操作中,就应该如此。
3) 我将数组用于带有ID的结构,这些是我所遭受的无法预料的后果:
我最终改变了我的数据结构,重写了很多代码。你已经被警告过了,请不要给自己惹麻烦。
也:
4)大多数带有ID的集合都是为了使用ID作为对整个对象的引用,你应该利用这一点。API调用将获得ID,然后是其余的参数,操作和减速器也是如此。
问题1:reducer的简单性是因为不必搜索数组来找到正确的条目。不必搜索整个阵列是优势。选择器和其他数据访问器可能而且经常通过id
访问这些项目。必须在阵列中搜索每次访问,这将成为一个性能问题。当阵列变大时,性能问题会急剧恶化。此外,随着你的html" target="_blank">应用程序变得越来越复杂,在更多的地方显示和过滤数据,问题也变得更加严重。这种组合可能是有害的。通过使用id
访问项目,访问时间从O(n)
更改为O(1)
,这对于大型n
(此处为数组项目)而言会产生巨大的差异。
问题2:您可以使用normalizer
帮助您将API转换为存储。从normalizer V3.1.0开始,您可以使用反规范化来实现另一个目的。这就是说,应用程序的消费者往往多于数据生产者,因此,向存储的转换通常更频繁。
问题3:使用阵列时遇到的问题与其说是存储约定和/或不兼容的问题,不如说是性能问题。
问题内容: 对于我正在使用的插件,我必须具有如下所示的状态: 我如何在不设置其余访问权限的情况下设置hospital_id的状态? 这似乎删除了除了hospital_id之外的所有内容: 问题答案: 您有几种选择: 借助ECMA6,您可以使用对象传播建议()创建具有更新属性的对象的副本。 您可以在对象()上使用本机分配功能 或为最短的版本和原子更新: 还有一个选项是更新插件: 我建议使用第一个。
`正在尝试更新onclick中的对象数组。我有两个状态,即数组格式的“IntialValue”和“Todos”。当我检查react developer工具时,我可以看到有两种状态。该任务的流程是用户可以在输入文本框中输入文本,一旦用户点击了添加按钮,我想把它添加为对象数组,我保留了一个布尔字段和Id字段。我面临的问题是我能正确地得到“InputValue”。我还得到了对象的数组,但不是在“todo
问题内容: 假设您有一个非常简单的数据结构: …并且您想将其中一些存储在javascript变量中。如我所见,您有三个选择: 如果您要存储(或希望可能拥有)多个“价值”部分(例如,增加他们的年龄等),显然第二或第三种选择是可行的,因此,为了论证,让我们假设在此结构中再也不需要任何数据值了。您选择哪一个,为什么? 编辑 :该示例现在显示最常见的情况:非顺序ID。 问题答案: 每个解决方案都有其用例。
对于我正在使用的插件,我必须具有如下状态: 如何在不设置其余访问权限的情况下设置hospital_id状态? 这似乎删除了一切,但hospital_id:
我正在寻找规范化的一些数据从API返回。已经在项目中使用下划线。 我希望创建一个如下所示的数据结构: 再次感谢所有的答案!
问题内容: 问题在于确定以下符号之间的权衡: 基于JSON : 基于数组 : 关于同一问题的这篇文章,我已经决定(在前端)使用JSON对象表示法而不是对象数组,因为它符合我的要求,更好的性能和更少的浏览器代码。 但是问题在于列表本身不是静态的。我的意思是,该列表正在生成,即从DB(NoSQL)获取/存储,并通过服务器上的JavaAPI为新条目创建。我无法决定在后端应使用哪种表示法(最终也会影响UI