数组数据的规范化状态形状必须涉及对象数组和嵌套对象之间的往返转换
Must normalizing state shape for array data involve round-trip conversion between object array and nested object
根据 normalizing state shape 上的文档,列表的数据本质上是通过 Id 作为对象存储在状态树中的,这意味着从 API 调用返回的数组数据必须首先被转换反对行动创造者。然后必须在 mapStateToProps() 中将对象数据转换回数组,以便使用 render() 中数组的 .map() 函数渲染生成的道具。这种来回转换是否是标准化状态形状的合理成本?或者在使用 ReactJS 和 Redux 时是否有更好的设计模式来处理列表数据?
是的,你所描述的是如果你存储规范化状态然后希望你的组件使用一个数组你必须做什么。
通常我的状态是这样的:
users: {
byId: {
// map of objects keyed by id
},
ids: [//array of ids],
}
请注意,我保留了 id 以跟踪从服务器返回项目的顺序;根据用例,这可能是必要的,也可能不是必要的。如果顺序不重要(可能是因为您最终按其他标准排序),您可以只存储对象并使用 Object.keys。
通常,最易用的数据形式是数组,因此您可以定义一个选择器来获取用户。按照 colocating reducers and selectors 的模式,您将有
// User reducer
export const getUsers = (state) => state.ids.map(id => state.byId[id])
和
// Root reducer
import users, * as fromUsersReducer from './users
const reducer = combineReducers({
users,
})
export const getUsers = (state) => fromUsersReducer(state.users)
然后是你的容器
import { connect } from 'react-redux'
import { getUsers } from 'reducers/root'
import UsersComp from './users
const mapStateToProps = (state) => ({
users: getUsers(state),
})
export default connect(mapStateToProps)(UsersComp)
就此转换是否合理而言,这取决于用例。当您的数据可以编辑,或者您的数据与其他数据相关(从用户获取所有帖子等)时,规范化状态形状变得非常重要。但是,如果您所做的只是获取一个数据列表,而您对它所做的只是显示该列表,那么规范化可能就没有必要了。
如果您最终需要为数据添加更复杂的操作,假设您严格要求仅通过选择器访问状态,则很容易切换。归一化数据通常被视为最佳实践,因为您通常最终会用数据做更复杂的事情,而归一化的形状是有利的。但这不是硬性规定或任何东西;有些情况下确实不需要。
您在这里看到的唯一真实成本是
- 执行 normalization/denormalization 的代码。
- 如果您的列表很长,性能会受到影响,必须在容器的每个渲染器上进行非规范化。
您可以使用 reselect 轻松减轻性能损失。这通常是不必要的。至于多余的代码,基本上就是一遍又一遍的同种东西,所以可以很容易抽象出来,反正代码本身其实也不复杂。如果所有内容都存储为数组,那么它比 reducer/selector 复杂应用程序中的代码要简单得多。
根据 normalizing state shape 上的文档,列表的数据本质上是通过 Id 作为对象存储在状态树中的,这意味着从 API 调用返回的数组数据必须首先被转换反对行动创造者。然后必须在 mapStateToProps() 中将对象数据转换回数组,以便使用 render() 中数组的 .map() 函数渲染生成的道具。这种来回转换是否是标准化状态形状的合理成本?或者在使用 ReactJS 和 Redux 时是否有更好的设计模式来处理列表数据?
是的,你所描述的是如果你存储规范化状态然后希望你的组件使用一个数组你必须做什么。
通常我的状态是这样的:
users: {
byId: {
// map of objects keyed by id
},
ids: [//array of ids],
}
请注意,我保留了 id 以跟踪从服务器返回项目的顺序;根据用例,这可能是必要的,也可能不是必要的。如果顺序不重要(可能是因为您最终按其他标准排序),您可以只存储对象并使用 Object.keys。
通常,最易用的数据形式是数组,因此您可以定义一个选择器来获取用户。按照 colocating reducers and selectors 的模式,您将有
// User reducer
export const getUsers = (state) => state.ids.map(id => state.byId[id])
和
// Root reducer
import users, * as fromUsersReducer from './users
const reducer = combineReducers({
users,
})
export const getUsers = (state) => fromUsersReducer(state.users)
然后是你的容器
import { connect } from 'react-redux'
import { getUsers } from 'reducers/root'
import UsersComp from './users
const mapStateToProps = (state) => ({
users: getUsers(state),
})
export default connect(mapStateToProps)(UsersComp)
就此转换是否合理而言,这取决于用例。当您的数据可以编辑,或者您的数据与其他数据相关(从用户获取所有帖子等)时,规范化状态形状变得非常重要。但是,如果您所做的只是获取一个数据列表,而您对它所做的只是显示该列表,那么规范化可能就没有必要了。
如果您最终需要为数据添加更复杂的操作,假设您严格要求仅通过选择器访问状态,则很容易切换。归一化数据通常被视为最佳实践,因为您通常最终会用数据做更复杂的事情,而归一化的形状是有利的。但这不是硬性规定或任何东西;有些情况下确实不需要。
您在这里看到的唯一真实成本是
- 执行 normalization/denormalization 的代码。
- 如果您的列表很长,性能会受到影响,必须在容器的每个渲染器上进行非规范化。
您可以使用 reselect 轻松减轻性能损失。这通常是不必要的。至于多余的代码,基本上就是一遍又一遍的同种东西,所以可以很容易抽象出来,反正代码本身其实也不复杂。如果所有内容都存储为数组,那么它比 reducer/selector 复杂应用程序中的代码要简单得多。