重新选择——创建一个仅用于获取部分状态的记忆选择器是否有意义?

Reselect - does it ever make sense to create a memorized selector which is just used to get part of the state?

我有一个普通的选择器,它只是用来获取状态的一部分:

export const getAllPosts = state => {
  return state.posts;
};

如果我使用重新选择来包装选择器:

import { createSelector } from 'reselect';

export const allPosts = createSelector(
  getAllPosts,
  (posts) => posts
);

提高性能等有意义吗?在我看来,包装器是不必要的。

不,这没有任何实际好处。

我发现我的第一级选择器只是简单的函数,任何比这更深的选择器都需要记忆:

// assume state is:  {first : {second {} } }

const selectFirst = state => state.first;

const selectSecond = createSelector(
    selectFirst,
    first => first.second
);

不,仅仅为了获取状态树的一部分而创建记忆化选择器没有意义。

原因是 connect 会对从 mapStateToProps 传入的每个 prop 进行自己的浅层相等性检查。如果由选择器编辑的 return 属性和其他属性一起通过了浅层相等性检查,那么 render 将不会被不必要地调用。如果选择器只是 returned 状态树的一部分并且状态树的那部分没有被修改,那么浅层相等性检查就足够了。

但是,如果选择器是根据其他选择器的结果计算得出的,那么使用 createSelector 是一个不错的选择。首先,它为组合选择器提供了一个很好的语法。其次,如果组合选择器的计算可能很昂贵,您将获得性能优势。第三,如果选择器是 return 一个新的但等价的对象或数组,那么 connect 提供的浅层相等性检查是不够的。在那种情况下,createSelector 提供的记忆将确保相同的对象或数组实例在输入未更改的情况下被 returned,然后浅层相等性检查将足以避免代价高昂的重新渲染。

因此,仅公开部分状态树 createSelector 不会添加任何内容。

对于几乎所有从状态树的多个部分计算的选择器,createSelector 开始增加价值。它添加的价值量因选择器而异,从更易于阅读到确保您不会不必要地重新渲染组件树。

在你的情况下,它没有意义,因为你只是 return 来自商店的相同引用,除非你修改它,否则它总是浅等于它自己(或它以前的状态)。

如果您想象一个不同的场景,例如,您将实体存储在一个对象中而不是存储中的数组中,但您想要 return 一个数组到您的组件,那么您需要派生数据:

export const selectAllPosts = createSelector(
  getAllPostsFromStore, // returns { 1: {...}, 2: {...} }
  (allPosts) => Object.keys(allPosts).map(key => allPosts[key])
);

现在你的选择器变成了性能提升,因为它只在商店中的某些东西发生变化时计算派生数据。

所以我的经验法则是:如果你不推导数据,就不需要记忆它。