简评:使用 redux 最普遍的问题之一就是如何组织 store 的数据结构,作者根据最佳实践,改造 store 的最好的方式就是——添加索引。
一些常见的方式
如果你存储的每条数据都有一个 id,你可以使用数组,对象或者对象数组的方式来存储。
扁平对象的数组 [{values}] :
categories: [
{name: 'abs', id: '32o8wafe', exercises: ['crunches', 'plank']},
{name: 'arms', id: 'oaiwefjo', exercises: [...]},
{name: 'legs', id: 'aoijwfeo', exercises: [...]},
]
这是我目前为止见过最常见的一种方式。这让迭代很方便,但是不通过迭代和过滤就不能够快速地查询一条切确的数据。
对象的键是 id {id: {values}} :
categories: {
32o8wafe: {name: 'abs', exercises: ['crunches', 'plank']},
oaiwefjo: {name: 'arms', exercises: [...]},
3oij2e3c: {name: 'legs', exercises: [...]},
}
这样你访问每条数据花费的时间仅需要 O(1),但是当你使用 Object.values()方式迭代的时候,你不能轻松拿到给定数据的 id。
对象的数组加上 id [{id: {values}}]:
categories: [
{32o8wafe: {name: 'abs', exercises: [...]},
{oaiwefjo: {name: 'arms', exercises: [...]},
{3oij2e3c: {name: 'legs', exercises: [...]},
]
这样可以让你用 id 和 values 轻松地迭代列表,但是它不能给你快速的 O(1) 的访问时间,因为它本质上是个数组(表示不是很理解)。
像数据库用 id 来索引每行那样结构化
我们在 Monadical 学习 redux 的时候,我们发现了既能轻松地使用Object.values(state.categories)来进行迭代,又能只花费 O(1) 的时间快速地访问每项数据:
categories: {
32o8wafe: {id: '32o8wafe', name: 'abs', exercises: [...]},
oaiwefjo: {id: 'oaiwefjo', name: 'arms', exercises: [...] },
3oij2e3c: {id: '3oij2e3c', name: 'legs', exercises: [...] },
}
注意到 id 既是每行的键,又是行本身的属性。一点点的冗余给我们提供了极大的便利。这种方式被 redux 的文档推荐,同时兼容 normalized (又名 扁平化)形式。
现在你可以在迭代的时候访问 id:
Object.values(categories).map(cat => ({id: cat.id, name: cat.name}))
或者用 id 瞬间访问单条数据:
categories[category_id].name
我们已经向前端发送像这样的数据了,所以前端不需要做任何容易出错的处理来产生键值对的映射。在后端很容易做,因为从数据库中取出数据时已经含有 id 字段了,所以你可以把它作为键。
索引的力量
注意到我们上面介绍的形式只是一些行的索引,id 就是索引。你的 store 要是像这样的话,你同样可以生成索引,让你能够只花费 O(1) 的代价来访问数据:
用名字来索引目录:
categories_by_name = {
abs: '32o8wafe',
arms: 'oaiwefjo',
legs: '3oij2e3c',
}
const get_category_by_name = (store, name) =>
store.getState().categories[categories_by_name[name]]
对于相同的数据你可以随心所欲地构造索引的数量,基于任何列的访问都只要 O(1),就像在数据库中一样。
Normalizr & Reselect
我在这里描述的模式正是 Normalizr 这个库所使用的模式。如果你要扁平化(用类型区分)地储存你的数据,并且像上面介绍的索引的概念,阅读一下 Redux Without Profanity docs关于 Normalizr 的文档。
使用 reselect 可以很好地将 Redux 和 Normalizr 结合起来,如果你在乎性能并且喜欢有记录选择器的中心列表,就可以去看看。
拓展阅读:
- designing-the-state-shape
- NormalizingStateShape.html
- javascript-redux-normalizing-the-state-shape
- how-to-choose-the-redux-state-shape-for-an-app-with-list-detail-views-and-pagina
- how-to-get-best-practice-react-redux-nested-array-data
- how-to-store-your-state-data-f17ceca37aa
- normalizer
原文地址:Shape your redux store like your database
延伸阅读:如何把水变成 3D 移动影像