构建有效的 Redux Store
在构建 Redux store 时,需要记住以下两个事项:
数据不要重复。如果数据存储在多个位置,就没有单一数据源,就会浪费资源来使数据相互同步。
store 尽量很浅。嵌套数据使 reducer 逻辑更复杂(尝试更新深度嵌套的数据会变得很复杂,并且很慢)
我们来看一个简单的示例。下面是一个 people 对象和一个 friends 数组。
const people = {
kassidi: {
name: 'Kassidi Henry',
age: 24,
favoriteMovie: 'Remember the Titans'
},
tyler: {
name: 'Tyler McGinnis',
age: 25,
favoriteMovie: 'Fatigue: A JavaScript Story'
},
jake: {
name: 'Jake Lingwall',
age: 26,
favoriteMovie: 'Casablanca'
},
}
const friends = ['kassidi', 'jake']
现在,如果我想创建引用所有好友的新数组,代码很简单:
friends.map((friend) => people[friend])
我再也不会遇到数据一致性错误,因为我的所有数据只是引用其他数据。Redux 中的数据就应该这样。应该尽量避免数据重复并创建引用。
Redux 文档对这一模式进行了完美的总结:
“在更加复杂的应用中,需要让不同的实体相互引用。建议使状态尽可能标准化,没有任何嵌套。使对象中的每个实体存储时都具有 ID(作为键),并在其他实体或列表中使用 ID 引用它们。”
下一个提示是让 store 中的状态尽可能的浅,这样可以提高性能并降低复杂性。
假设有个如下所示的对象:
const books = {
fiction: {
fantasy: {
teens: {
0: {
title: 'Harry Potter and the Nested Data',
author: 'JK Rowling',
}
},
adults: {}
},
romance: {},
scifi: {},
},
nonFiction: {}
}
如果我们想要创建新的对象(因为我们从未希望修改原始状态),但是修改 Harry Potter 的标题,reducer 函数就会如下所示:
function books (state, action) {
const { bookType, genre, category, id, title } = action
if (action.type = CHANGE_TITLE) {
return {
...state,
[bookType]: {
...state[bookType],
[genre]: {
...state[bookType][genre]: {
[id]: {
...state[bookType][genre][id],
title,
}
}
}
}
}
}
return state
}
看看这个嵌套结构,呃!你懂得。不仅非常不高效,因为我们每次克隆状态时,都要使用操作符 (...
) ,而且无论对于代码编写者还是阅读者来说,都十分难以阅读。
通过引用状态中的不同实体,并尽量减少状态的嵌套,可以提高应用性能,也能使你的代码更易于阅读。
标准化总结
标准化 是指删除重复数据并尽量减少代码嵌套。这样不仅使应用在 store 的状态中保持“单一数据源”——更新状态的 reducer 逻辑也能保持整洁、合理。最终,标准化 Redux store 将使查询更高效和一致。
更多资料
- Normalizr
- Redux 文档中的 State 范式化 / 英 部分