当我将正常状态映射到组件时是否需要重新选择?

时间:2017-10-24 12:58:14

标签: reactjs redux reselect

我是新手重新选择,我理解需要。我认为这太棒了。但是,就我而言,它似乎无缘无故地添加了许多额外的代码。也许我做错了。

上一个组件:

const mapStateToProps = (state) => {
    return {
        day: state.filters.day,
        minDate: state.filters.minDate,
        maxDate: state.filters.maxDate,
    };
};

现在选择器:

import { createSelector } from 'reselect';

const getDay = state => state.filters.day;
export const makeGetDay = () => createSelector(
    getDay,
    day => day,
);

const getMinDate = state => state.filters.minDate;
export const makeGetMinDate = () => createSelector(
    getMinDate,
    date => date,
);

const getMaxDate = state => state.filters.maxDate;
export const makeGetMaxDate = () => createSelector(
    getMaxDate,
    date => date,
);

现在组件:

const makeMapStateToProps = () => {
    const getDay = makeGetDay();
    const getMinDate = makeGetMinDate();
    const getMaxDate = makeGetMaxDate();
    return state => ({
        day: getDay(state),
        minDate: getMinDate(state),
        maxDate: getMaxDate(state),
    });
};

澄清,代码有效,我只是不明白Reselect在这种情况下添加了什么..

2 个答案:

答案 0 :(得分:2)

首先 - 您无需在任何地方使用高阶函数来使用reselect。看起来你正在做不必要的步骤。

摆脱高阶函数:

下面的代码用同样的方式表达,但看起来更具可读性(紧凑)

export const getDay = createSelector(
    state => state.filters.day,
    day => day,
);

export const getMinDate = createSelector(
    state => state.filters.minDate,
    date => date,
);

export const getMaxDate = createSelector(
    state => state.filters.maxDate,
    date => date,
);

const mapStateToProps = state => ({
  day: getDay(state),
  minDate: getMinDate(state),
  maxDate: getMaxDate(state),
});

你可以走得更远。看起来你在转换组件时要重复几次变量。

store.maxDate -> date -> maxDate

您可以让“选择器”不仅负责从商店检索数据,还负责遵循命名约定。

export const getDay = createSelector(
  state => state.filters.day,
  day => ({ day }),
);

export const getMinDate = createSelector(
  state => state.filters.minDate,
  minDate => ({ minDate }),
);

export const getMaxDate = createSelector(
  state => state.filters.maxDate,
  maxDate => ({ maxDate }),
);

const mapStateToProps = state => ({
  ...getDay(state),
  ...getMinDate(state),
  ...getMaxDate(state),
});

如果你想根据以前的选择再创建一个选择器,那么composition选择器的好处就会变得更加清晰:

假设当day超出maxDateminDate

定义的范围时,您有一个显示错误的组件
export const isDateOutOfRange = createSelector(
  getDay,
  getMinDate,
  getMaxDate,
  ({ day }, { minDate }, { maxDate }) => day > maxDate || day < minDate,
);

const mapStateToProps = state => ({
  showError: isDateOutOfRange(state),
});

答案 1 :(得分:1)

如果您在问题中指定Reselect,则实际上并未添加任何值。

原因是connect提供的react-redux会对您mapStateToProps函数中提供的道具进行浅层比较,以确定是否需要渲染。在您提供的示例中,如果dayminDatemaxDate的值未发生变化,则不会浪费任何时间进行不必要的渲染。

Reselect的实际值在您的选择器返回计算的内容时出现。

借用弗拉德的一个例子。 Reselect非常适合编写选择器,因此您的选择器可能如下所示:

export const getDay = state => state.filters.day;
export const getMinDate = state => state.filters.minDate;
export const getMaxDate = state => state.filters.maxDate;

export const getIsDateOutOfRange = createSelector(
  getDay,
  getMinDate,
  getMaxDate,
  (day, minDate, maxDate) => day > maxDate || day < minDate
);

您的mapStateToProps功能可能如下所示:

const mapStateToProps = state => ({
    isOutOfRange: getIsDateOutOfRange(state)
});

在这种情况下,Reselect提供了一个很好的语法,用于组合选择器和边际性能优势,因为getIsDateOutOfRange只有在其中一个依赖选择器返回不同的值时才会被重新计算。

Reselect隐藏了性能优势。 如果您有一个返回计算数组或对象的选择器,那么从选择器返回的两个相同值将不会通过Reselectconnect将用于记忆目的的浅等式检查。

[0, 1] === [0, 1] // false

所以对于一个人为的例子:

export const getDays = state => state.filters.days;
export const getMinDate = state => state.filters.minDate;
export const getMaxDate = state => state.filters.maxDate;

export const getDaysWithinRangeNotPerformant = state => {
    const days = getDays(state);
    const minDate = getMinDate(state);
    const maxDate = getMaxDate(state);
    return days.filter(day => day > minDate && day < maxDate);
};

export const getDaysWithinRangePerformant = createSelector(
    getDay,
    getMinDate,
    getMaxDate,
    (days, minDate, maxDate) =>
        days.filter(day => day > minDate && day < maxDate)
);

此处Reselect解锁的性能优势是双重的。

首先即使多次调用getDaysWithinRangePerformant,只有在实际参数发生变化时才会执行可能很昂贵的filter

其次,最重要的是,getDaysWithinRangeNotPerformant每次调用connect时都会返回一个新数组,这意味着connect中道具的浅层比较将为假,{即使实际天数没有改变,也会再次调用{1}}。因为rendergetDaysWithinRangePerformant记忆,如果值没有改变,它将返回完全相同的数组实例,因此createSelector中道具的浅比较将为真,并且它将能够执行它自己的优化并避免不必要的渲染。

我认为这是connect提供的巨大好处。