开发者问题收集

重新选择 createSelector 问题

2018-07-12
4740

尝试使用 Redux 的 Reselect 库,如下所示:

// selectors.js
import { createSelector } from "reselect";

const loggedIn = state => {
  return state.language.loggedIn;
};

export const loggedInSelector = createSelector(loggedIn);

用法如下:

import React from "react";
import { connect } from "react-redux";
import { Text, View } from "react-native";
import { loggedInSelector } from "./../lib/selectors/language";

const Foo = ({ loggedIn }) => {
  return (
    <View>
      <Text>Foo - {loggedIn ? "logged in" : "not logged in"}</Text>
    </View>
  );
};

export default connect(state => {
  console.log(state);
  const loggedIn = loggedInSelector(state);
  return {
    loggedIn
  };
})(Foo);

console.log(state) 的结果是:

language: {
  loggedIn: false
}

但是,我收到类型错误:无法读取未定义的属性“语言”。有人能看出我在这个我认为相当简单的 Reselect 用例中哪里出错了吗?

2个回答

正如您在自己的回答中指出的那样,您缺少转换函数。

const getLoggedIn = state => state.language.loggedIn
export const getloggedInMemoized = createSelector(loggedIn, i => i)

无法读取未定义的属性“语言” 错误与您的 Reducer 函数的初始状态有关。

在您的记忆选择器中,转换函数只是身份函数 i => i ,这是一个非常强烈的指标,表明您传入的选择器 ( const logstIn = state => state.language.loggedIn ) 不需要记忆。为什么?因为 react-reduxconnect 会在内部进行自己的相等性检查,以确保只有当一个或多个 props 发生变化时,才会重新渲染组件。

基本上,您的初始 loggedIn 选择器完全没问题,并且不需要 createSelector 的额外开销(无论多小)。

何时使用 createSelector

  1. 任何时候您组合两个或多个选择器的结果 ,因为 createSelector 的语法确实有助于构建这些选择器。 例如。

    const taxSelector = createSelector(
    subtotalSelector,
    taxPercentSelector,
    (subtotal, taxPercent) => subtotal * (taxPercent / 100)
    )
    
  2. 任何时候选择器执行计算量大的操作时 createSelector 将确保仅当至少一个输入选择器的结果发生变化时才调用转换函数。

  3. 在我看来,这是微妙但关键的原因。 当选择器的返回值是 数组对象 时。这对于性能非常重要。 react-reduxconnect 函数将对提供的 props 执行简单的相等性检查,以查看组件是否需要重新渲染(加起来很昂贵)。例如。

    const getSaleItems = state => {
    const items = getShopItems(state)
    return items.filter(i => i.isOnSale)
    }
    

    每次调用 getSaleItems 时,它都会返回一个新数组。即使 getShopItems 返回的数组没有改变。这意味着 react-redux 将重新渲染组件,因为当它对提供的 props 进行浅层相等性检查时,它将看到 数组已更改。

    另一方面,当使用 createSelector 时:

    const getSaleItems = createSelector(
    getShopItems,
    items => items.filter(i => i.isOnSale)
    }
    

    如果 getShopItems 的结果没有改变,则对 getSaleItems 的连续调用将返回相同的数组实例,因为结果已被记忆。这意味着 react-redux 的浅层相等性检查将发现没有任何变化,并且它不需要重新渲染组件🎉。

Chad Edrupt
2018-08-01

结果发现,我缺少 createSelector 的(看似必需的)第二个参数:

export const logstedInSelector = createSelector(loggedIn, l => l);

benhowdle89
2018-07-12