【问题标题】:Redux build index of objectRedux 构建对象的索引
【发布时间】:2017-09-05 14:59:30
【问题描述】:

在 redux 应用程序中,可以说它是一个博客。 状态可能看起来像

{
  Posts: {
    1:{day:'2016-03-13', id:1},
    2:{day:'2016-03-14',id:2},
    .....
  }
}

现在在某个组件中,我想显示特定日期的所有帖子,我可以使用 Array.filter 过滤所有帖子以获取当天的帖子,但这意味着如果我每次组件刷新时都有 1000 条帖子将重新计算整个过滤器。

所以在这种情况下,我认为如果我在 redux 存储中有类似的索引会更好

{
  PostsByDate: {
    '2017-03-13': [1,2], .. Etc
  }
}

那么如何建立这样的索引并确保它始终与帖子对象同步?

【问题讨论】:

    标签: design-patterns redux react-redux flux


    【解决方案1】:

    如果您在客户端存储了大量数据(+1000 篇博客文章),您可能会考虑在服务器端进行分页和运行过滤器。过滤器的计算成本应该很低,但这实际上取决于您使用的过滤器类型,如果您使用每篇博客文章的正文进行过滤,您很可能会遇到性能问题。但是,如果过滤的成本很低,则将 id 数组与完整的帖子集合连接起来的成本可能非常相似,因此您提出的架构应该不会有显着的性能提升。

    话虽如此,我会通过在数组中显示要显示的帖子 ID 集合来做到这一点,并在每次过滤器更改时更新该数组。如果没有过滤器,则数组为undefined,您会显示所有帖子。

    动作创建者

    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
        }
      ...
    }
    

    不要纠结于收藏,使用Immutable

    【讨论】:

      【解决方案2】:

      官方 redux 文档中推荐的一件事是为您的数据构建索引。我收集状态的最小结构是

      Map({
        byId: Map(),
        allIds: List([])
      })
      

      byId 映射基本上就是您在 Posts 哈希中拥有的内容。将您的 redux 存储视为客户端数据库,如果您至少有一些后端经验 IMO,这种思维模式真的很有帮助。按键查询地图是 O(1) 操作,所以这可以让您非常快速地按 id 获取(想想SELECT * FROM POSTS WHERE id = your_id LIMIT 1)。

      现在,allIds 只是 byId 中的键的所有 id 的列表,当您显示数据列表时很有用 - 列表组件只有 id 列表并将每个单独的 id 传递给子 ListItem 组件,它们还连接到 redux 存储并使用简单的选择器在其 mapStateToProps 函数中获取单个项目:selectPost(state, postId) { return state.posts.getIn(['byId', postId]) }

      这会给你带来巨大的性能提升,特别是如果 PostsList 组件使用 re-select 函数来缓存 id 的获取列表,因为如果你重新排序集合,React 会使用 key 属性以智能方式处理它,应该发布id 在你的情况下 - 所以重新渲染非常快。此外,如果某些帖子发生更改,则只需要重新渲染这个单独的帖子组件。

      您可以通过为您的特定用例引入额外索引来进一步采用这种方法:如果您想显示某个用户的所有帖子,您可以添加byUserId: Map(<lists of post ids>) 以便在这种情况下更快地查找。就像关系数据库索引一样,它是相同的概念。

      在 Rails 中,每个模型都有 updated_at 时间戳字段,因此简单的缓存可以检查后端获取的集合的最新 updated_at 并将其与标头进行比较,您的客户端可以将其与所有 api 请求一起发送,如果注释晚于客户端值,则客户端数据不会过时,后端可以立即响应相应的标头,响应时间大约为 2-5 毫秒,然后客户端可以完全绕过获取 json,解析它并提供给减速器功能,因此客户端的性能也有很多提升。这样,您的组件就可以在 componentDidMount 上触发 api 请求,而无需任何复杂的逻辑,而缓存层将处理其余部分

      【讨论】:

      • 另外,你应该完全使用 immutable.js 来进行快速和健全的收集。我所有的模型基本上都是 immutable.js 记录,与原生 javascript 集合相比,使用集合是天堂。不变性也确实有助于提高性能。请参阅 React.PureComponent 和 mapStateToProps 文档,了解 redux 如何为连接的组件实现 shouldComponentUpdate。此外,分页是必不可少的,加上后端中的某种 http 缓存 + 客户端上的简单缓存机制来绕过所有这些 api 调用有很大帮助
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-04-02
      • 1970-01-01
      • 2021-12-28
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多