重新选择将无法正确记忆同一组件的多个实例

Dáv*_*nár 4 javascript redux react-redux reselect

我正在阅读Redux的文档并且被困在了reselect.下面的代码创建了一个选择器和文件说,如果我们想用它两个VisibleTodoList部件,那么它不会工作正常.

import { createSelector } from 'reselect'

const getVisibilityFilter = (state, props) => state.todoLists[props.listId].visibilityFilter

const getTodos = (state, props) => state.todoLists[props.listId].todos

const getVisibleTodos = createSelector([getVisibilityFilter, getTodos], (visibilityFilter, todos) => {
  switch (visibilityFilter) {
    case 'SHOW_COMPLETED':
      return todos.filter(todo => todo.completed)
    case 'SHOW_ACTIVE':
      return todos.filter(todo => !todo.completed)
    default:
      return todos
  }
})

export default getVisibleTodos
Run Code Online (Sandbox Code Playgroud)

将getVisibleTodos选择器与visibleTodoList容器的多个实例一起使用将无法正确地进行memoize

const mapStateToProps = (state, props) => {
  return {
     // WARNING: THE FOLLOWING SELECTOR DOES NOT CORRECTLY MEMOIZE
     todos: getVisibleTodos(state, props)
  }
}
Run Code Online (Sandbox Code Playgroud)

这是什么意思?我无法弄清楚为什么它不起作用.

mar*_*son 13

正确.那是因为默认情况下重新选择只记忆最新的输入集:

const a = someSelector(state, 1); // first call, not memoized
const b = someSelector(state, 1); // same inputs, memoized
const c = someSelector(state, 2); // different inputs, not memoized
const d = someSelector(state, 1); // different inputs from last time, not memoized
Run Code Online (Sandbox Code Playgroud)

在这些情况下,选择器仍然会检索数据,它只需重新计算结果,即使它在过去的某个时刻看到了输入.

因此,如果您在mapState函数中使用选择器并且它引用了一个值ownProps,那么该组件的多个实例可能会导致选择器永远不会正确地进行memoize

const mapState = (state, ownProps) => {
    const item = selectItemForThisComponent(state, ownProps.itemId);

    return {item};
}


// later
<SomeComponent itemId={1} />
<SomeComponent itemId={2} />
Run Code Online (Sandbox Code Playgroud)

在该示例中,selectItemForThisComponent将始终使用(state, 1)(state, 2)背靠背调用,因此它将无法正确记忆.

一种解决方案是使用支持的"工厂函数"语法connect.如果mapState函数在第一次调用函数时返回函数,connect则将其用作实际mapState实现.这样,您可以为每个组件实例创建唯一的选择器:

const makeUniqueSelectorInstance = () => createSelector(
    [selectItems, selectItemId],
    (items, itemId) => items[itemId]
);    


const makeMapState = (state) => {
    const selectItemForThisComponent = makeUniqueSelectorInstance();

    return function realMapState(state, ownProps) {
        const item = selectItemForThisComponent(state, ownProps.itemId);

        return {item};
    }
}

export default connect(makeMapState)(SomeComponent);
Run Code Online (Sandbox Code Playgroud)

组件1和组件2都将获得它们自己的唯一副本selectItemForThisComponent,并且每个副本将通过一致的可重复输入进行调用,从而允许正确的记忆.

更新

我在我的博客文章Idiomatic Redux中扩展了这个答案:使用Reselect Selectors进行性能和封装.