Redux 构建对象索引
Redux build index of object
在 redux 应用程序中,假设它是一个博客。
状态可以看起来像
{
Posts: {
1:{day:'2016-03-13', id:1},
2:{day:'2016-03-14',id:2},
.....
}
}
现在在某个组件中我想显示特定日期的所有 posts,我可以使用 Array.filter 过滤所有 posts 以获得 posts这一天,但这意味着如果每次组件刷新时我有 1000 个 post,它将重新计算整个过滤器。
所以在这种情况下,我认为如果我在 redux 存储中有一个索引会更好
{
PostsByDate: {
'2017-03-13': [1,2], .. Etc
}
}
那么如何建立这样的索引并确保它始终与 post 的对象同步?
如果您在客户端存储了大量数据(+1000 个博客 posts),您可能会考虑在服务器端进行分页和 运行 过滤器。过滤器的计算成本应该很低,但这实际上取决于您使用的过滤器类型,如果您使用每个博客的正文进行过滤 post 很可能会遇到性能问题。但是,如果过滤的成本很低,则将 id 数组与 post 的完整 collection 连接起来的成本可能非常相似,因此该架构应该会带来显着的性能提升你正在求婚。
也就是说,我会通过在数组中显示 post 个 ID 中的 collection,并在每次过滤器更改时更新该数组来实现。如果没有过滤器,则数组为 undefined
并且您显示所有 posts.
动作创作者
function filterPosts(filter) {
return { type: "FILTER_POSTS", filter };
}
减速机
switch (action.type) {
...
case "FILTER_POSTS":
return {
...state,
PostsFiltered: action.filter
? Posts.filter(action.filter).map(p => p.id)
: undefined
};
...
}
容器
container = connect(
state => ({ ... }),
dispatch => bindActionCreators({
onFilter: filterPosts,
onRemoveFilter: filterPosts.bind(null, undefined)
}, dispatch)
)(Component);
分量
filter() {
// Use whatever filter you want here
this.props.onFilter(...);
}
removeFilter() {
this.props.onRemoveFilter();
}
render() {
...
{
this.props.postsFiltered
? this.props.posts.filter(p => this.props.postsFiltered.contains(p.id))
: this.props.posts
}
...
}
不要纠结于 collections,使用 Immutable
官方 redux 文档中推荐的一件事是为您的数据建立索引。 collection 状态的最小结构是
Map({
byId: Map(),
allIds: List([])
})
其中 byId
映射基本上就是您在 Posts
哈希中拥有的内容。将你的 redux store 想象成一个 client-side 数据库,如果你至少有一些后端经验 IMO,这种思维模式真的很有帮助。按键查询地图是 O(1) 操作,因此这可以让您通过 id 快速获取(想想 SELECT * FROM POSTS WHERE id = your_id LIMIT 1
)。
现在,allIds 只是一个所有 ID 的列表,它们是 byId
中的键,当您显示数据列表时很有用 - 列表组件只有 ID 列表并将每个单独的 ID 传递给 child ListItem
组件,它们也连接到 redux store 并使用简单的选择器 selectPost(state, postId) { return state.posts.getIn(['byId', postId]) }
在它们的 mapStateToProps
函数中获取单个项目。
这会给你带来巨大的性能提升,特别是如果 PostsList
组件使用 re-select 函数来缓存获取 id 列表,因为如果你重新排序 collection,React 会以智能方式处理它使用 key
道具,在你的情况下应该是 post id - 所以重新渲染非常快。此外,如果某些 post 更改,则只有这个 post 组件需要重新渲染。
您可以通过为您的特定 use-cases 引入额外索引来进一步采用这种方法:如果您想显示某些用户的所有 post,您可以添加 byUserId: Map(<lists of post ids>)
在这种情况下查找速度更快。和关系型数据库的索引完全一样,是同一个概念。
在 rails 中,每个模型都有 updated_at 时间戳字段,因此简单的缓存可以在后端检查最新的 updated_at 以获取 collection 并将其与 header,您的客户端可以发送所有 api 请求,如果它晚于客户端值,则客户端数据不会过时并且后端可以立即响应相应的 header,大约 2-5 毫秒响应,然后客户端可以完全绕过获取 json,解析它并提供给 reducer 函数,因此客户端的性能也得到了很大提升。这样您的组件就可以在 componentDidMount 上触发 api 请求,而无需任何复杂的逻辑,缓存层将处理其余的
在 redux 应用程序中,假设它是一个博客。 状态可以看起来像
{
Posts: {
1:{day:'2016-03-13', id:1},
2:{day:'2016-03-14',id:2},
.....
}
}
现在在某个组件中我想显示特定日期的所有 posts,我可以使用 Array.filter 过滤所有 posts 以获得 posts这一天,但这意味着如果每次组件刷新时我有 1000 个 post,它将重新计算整个过滤器。
所以在这种情况下,我认为如果我在 redux 存储中有一个索引会更好
{
PostsByDate: {
'2017-03-13': [1,2], .. Etc
}
}
那么如何建立这样的索引并确保它始终与 post 的对象同步?
如果您在客户端存储了大量数据(+1000 个博客 posts),您可能会考虑在服务器端进行分页和 运行 过滤器。过滤器的计算成本应该很低,但这实际上取决于您使用的过滤器类型,如果您使用每个博客的正文进行过滤 post 很可能会遇到性能问题。但是,如果过滤的成本很低,则将 id 数组与 post 的完整 collection 连接起来的成本可能非常相似,因此该架构应该会带来显着的性能提升你正在求婚。
也就是说,我会通过在数组中显示 post 个 ID 中的 collection,并在每次过滤器更改时更新该数组来实现。如果没有过滤器,则数组为 undefined
并且您显示所有 posts.
动作创作者
function filterPosts(filter) {
return { type: "FILTER_POSTS", filter };
}
减速机
switch (action.type) {
...
case "FILTER_POSTS":
return {
...state,
PostsFiltered: action.filter
? Posts.filter(action.filter).map(p => p.id)
: undefined
};
...
}
容器
container = connect(
state => ({ ... }),
dispatch => bindActionCreators({
onFilter: filterPosts,
onRemoveFilter: filterPosts.bind(null, undefined)
}, dispatch)
)(Component);
分量
filter() {
// Use whatever filter you want here
this.props.onFilter(...);
}
removeFilter() {
this.props.onRemoveFilter();
}
render() {
...
{
this.props.postsFiltered
? this.props.posts.filter(p => this.props.postsFiltered.contains(p.id))
: this.props.posts
}
...
}
不要纠结于 collections,使用 Immutable
官方 redux 文档中推荐的一件事是为您的数据建立索引。 collection 状态的最小结构是
Map({
byId: Map(),
allIds: List([])
})
其中 byId
映射基本上就是您在 Posts
哈希中拥有的内容。将你的 redux store 想象成一个 client-side 数据库,如果你至少有一些后端经验 IMO,这种思维模式真的很有帮助。按键查询地图是 O(1) 操作,因此这可以让您通过 id 快速获取(想想 SELECT * FROM POSTS WHERE id = your_id LIMIT 1
)。
现在,allIds 只是一个所有 ID 的列表,它们是 byId
中的键,当您显示数据列表时很有用 - 列表组件只有 ID 列表并将每个单独的 ID 传递给 child ListItem
组件,它们也连接到 redux store 并使用简单的选择器 selectPost(state, postId) { return state.posts.getIn(['byId', postId]) }
在它们的 mapStateToProps
函数中获取单个项目。
这会给你带来巨大的性能提升,特别是如果 PostsList
组件使用 re-select 函数来缓存获取 id 列表,因为如果你重新排序 collection,React 会以智能方式处理它使用 key
道具,在你的情况下应该是 post id - 所以重新渲染非常快。此外,如果某些 post 更改,则只有这个 post 组件需要重新渲染。
您可以通过为您的特定 use-cases 引入额外索引来进一步采用这种方法:如果您想显示某些用户的所有 post,您可以添加 byUserId: Map(<lists of post ids>)
在这种情况下查找速度更快。和关系型数据库的索引完全一样,是同一个概念。
在 rails 中,每个模型都有 updated_at 时间戳字段,因此简单的缓存可以在后端检查最新的 updated_at 以获取 collection 并将其与 header,您的客户端可以发送所有 api 请求,如果它晚于客户端值,则客户端数据不会过时并且后端可以立即响应相应的 header,大约 2-5 毫秒响应,然后客户端可以完全绕过获取 json,解析它并提供给 reducer 函数,因此客户端的性能也得到了很大提升。这样您的组件就可以在 componentDidMount 上触发 api 请求,而无需任何复杂的逻辑,缓存层将处理其余的