我使用React / Redux / Reselect。
从reselect doc开始,如果我有多个组件将使用具有不同参数的选择器,则需要为每个实例创建一个。
const makeGetVisibleTodos = () => {
return createSelector(
[
(state, props) => props.visibilityFilter,
state => state.todos,
],
(visibilityFilter, todos) => {
return todos.filter(td => td.visibility === visibilityFilter)
}
}
)
}
但是在我的情况下,listId
可以来自多个来源(例如props.listId
,props.location.match.listId
,props.location.search.listId
等)
所以我更喜欢写以下内容:
const makeGetVisibleTodos = listId => {
return createSelector(
[
state => state.todos,
],
(todos) => {
todos.filter(td => td.listId === listId)
}
}
)
}
并通过以下方式连接:
connect(
(state, ownProps) => ({
offerer: makeGetVisibleTodos(ownProps.location.match.listId)(state, ownProps),
}),
)
我很确定它能正常工作,但我不是100%肯定会正确记住:
如果两个组件使用相同的makeGetVisibleTodos
调用listId
,它们将具有2个不同的缓存值,对吗?那不是我想要的...
怎么样?
const makeGetVisibleTodos = listId => {
return createSelector(
state => state.todos,
state => listId,
(todos, listId) => {
todos.filter(td => td.listId === listId)
}
}
)
}
在这种情况下,当makeGetVisibleTodos(ownProps.listId)
时,makeGetVisibleTodos(ownProps.match.params.listId)
和ownProps.listId === ownProps.match.params.listId
是否共享相同的缓存值?
表达问题的另一种方法是:如何传递不(直接)依赖于state
和ownProps
但在记忆过程中是否相等的额外参数?
我还可以扩展ownProps
:
connect(
(state, ownProps) => ({
offerer: makeGetVisibleTodos()(state, Object.assign({}, ownProps, {listId: ownProps.location.match.listId}),
}),
)
但是发现它非常丑陋,这失去了重点...
答案 0 :(得分:1)
在上面发布的示例中,由于每次调用mapStateToProps
函数时都会创建一个重新选择选择器的新实例,因此无法正确地进行记忆。
请注意,在文档中mapStateToProps
函数也如何成为工厂函数(makeMapStateToProps
),并在返回实际的makeGetVisibleTodos
函数之前调用mapStateToProps
,就像这样。
const makeMapStateToProps = () => {
const getVisibleTodos = makeGetVisibleTodos()
const mapStateToProps = (state, props) => {
return {
todos: getVisibleTodos(state, props)
}
}
return mapStateToProps
}
此外,您不会在makeGetVisibleTodos()
调用中传递在选择器中使用的道具,而是在实际调用选择器本身时传递。
这将导致类似
const makeGetVisibleTodos = () => {
return createSelector(
state => state.todos,
state => (_, listId), // Here you have access to all the arguments passed
(todos, listId) => {
todos.filter(td => td.listId === listId)
}
}
)
}
然后您可以编写如下选择器:
const makeMapStateToProps = () => {
const getVisibleTodos = makeGetVisibleTodos()
const mapStateToProps = (state, props) => {
return {
todos: getVisibleTodos(state, props.params.match.listId),
todos2: getVisibleTodos(state, props.listId),
todos3: getVisibleTodos(state, 'hardcodedListId')
}
}
return mapStateToProps
}
答案 1 :(得分:1)
作为re-reselect(围绕reselect
的一个小包装)的作者,我想指出该库如何解决您的用例。
re-reselect
提供了一些选择器,这些选择器保留了跨不同组件的存储。
import createCachedSelector from 're-reselect';
const getVisibleTodos = createCachedSelector(
state => state.todos,
(state, listId) => listId,
(todos, listId) => {
todos.filter(td => td.listId === listId)
}
}
)(
(todos, listId) => listId, // Create/use a different selector for each different listId
);
像普通选择器一样使用它(在任何其他容器组件中),而不必考虑选择器工厂:
const makeMapStateToProps = () => {
const mapStateToProps = (state, props) => {
return {
todos: getVisibleTodos(state, props.params.match.listId),
todos2: getVisibleTodos(state, props.listId),
todos3: getVisibleTodos(state, 'hardcodedListId')
}
}
return mapStateToProps
}
re-reselect docs中也描述了该用例。
答案 2 :(得分:0)
由于调用makeGetVisibleTodos
时总是创建一个新的选择器,因此您的方法无法正确进行记忆。您可以通过写作来改进
const listIdQuerySelector = (state, props) => {
return props.match.params && props.match.params.listId;
};
const todoSelector = createSelector(
[
listIdQuerySelector,
state => state.todos,
],
(listId, todos) => {
todos.filter(td => td.listId === listId)
}
}
)
并像使用它
connect(
(state, ownProps) => ({
offerer: makeGetVisibleTodos(state, ownProps),
}),
)