当我只是将正常状态映射到组件时是否需要重新选择?
Is reselect needed when I just map the normal state to a component?
我是重新选择的新手,我了解这种需要。我认为这很棒。但是,在我的例子中,它似乎无缘无故地添加了很多额外的代码。也许我做错了。
上一个组件:
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 在这种情况下添加了什么..
首先 - 你不必为了使用 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
您可以让您的 "selectors" 不仅负责从存储中检索数据,还负责遵循命名约定。
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
超出 maxDate
和 minDate
定义的范围时显示错误
export const isDateOutOfRange = createSelector(
getDay,
getMinDate,
getMaxDate,
({ day }, { minDate }, { maxDate }) => day > maxDate || day < minDate,
);
const mapStateToProps = state => ({
showError: isDateOutOfRange(state),
});
在您在问题中指定的情况下 Reselect
实际上并没有增加任何价值。
原因是 react-redux
提供的 connect
将对 mapStateToProps
函数中提供的道具进行自己的浅层比较,以确定是否需要渲染。在您提供的示例中,如果 day
、minDate
或 maxDate
的值没有改变,您将不会在不必要的渲染上浪费任何时间。
当您的选择器 return 是经过计算的东西时,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
仅在其依赖选择器之一 [=64] 时才会重新计算这一事实而带来边际性能优势=]是一个不同的值。
Reselect
隐藏了性能优势。
如果你有一个选择器 return 是一个计算数组或一个对象,那么从选择器 return 得到的两个相同值将不会通过 Reselect
或 connect
会通过的浅层相等检查用于记忆目的。
[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
仅在实际参数已更改时才执行。
其次,也是最重要的,每次 connect
调用 getDaysWithinRangeNotPerformant
时,它都会 return 一个新数组,这意味着 [=15= 中的 prop 的浅比较] 将为 false,并且 render
将再次调用,即使实际日期本身没有改变。因为 getDaysWithinRangePerformant
被 createSelector
记住,如果值没有改变,它将 return 完全相同的数组实例,因此 connect
中的道具的浅比较将是真实的并且它将能够执行自己的优化并避免不必要的渲染。
在我看来,这是 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 在这种情况下添加了什么..
首先 - 你不必为了使用 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
您可以让您的 "selectors" 不仅负责从存储中检索数据,还负责遵循命名约定。
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
超出 maxDate
和 minDate
export const isDateOutOfRange = createSelector(
getDay,
getMinDate,
getMaxDate,
({ day }, { minDate }, { maxDate }) => day > maxDate || day < minDate,
);
const mapStateToProps = state => ({
showError: isDateOutOfRange(state),
});
在您在问题中指定的情况下 Reselect
实际上并没有增加任何价值。
原因是 react-redux
提供的 connect
将对 mapStateToProps
函数中提供的道具进行自己的浅层比较,以确定是否需要渲染。在您提供的示例中,如果 day
、minDate
或 maxDate
的值没有改变,您将不会在不必要的渲染上浪费任何时间。
当您的选择器 return 是经过计算的东西时,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
仅在其依赖选择器之一 [=64] 时才会重新计算这一事实而带来边际性能优势=]是一个不同的值。
Reselect
隐藏了性能优势。
如果你有一个选择器 return 是一个计算数组或一个对象,那么从选择器 return 得到的两个相同值将不会通过 Reselect
或 connect
会通过的浅层相等检查用于记忆目的。
[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
仅在实际参数已更改时才执行。
其次,也是最重要的,每次 connect
调用 getDaysWithinRangeNotPerformant
时,它都会 return 一个新数组,这意味着 [=15= 中的 prop 的浅比较] 将为 false,并且 render
将再次调用,即使实际日期本身没有改变。因为 getDaysWithinRangePerformant
被 createSelector
记住,如果值没有改变,它将 return 完全相同的数组实例,因此 connect
中的道具的浅比较将是真实的并且它将能够执行自己的优化并避免不必要的渲染。
在我看来,这是 Reselect
提供的最大好处。