【问题标题】:How to use Reselect selectors inside a Redux reducer如何在 Redux reducer 中使用 Reselect 选择器
【发布时间】:2017-08-07 13:07:09
【问题描述】:

我的应用已经拥有大量可供各种容器对象使用的选择器。这些非常适合访问状态的不同部分并使重构状态变得更加容易。

现在我想在我的一些 reducer 函数中使用我的选择器。问题在于,在 reducer 内部,state 参数指的是状态的特定切片,而选择器函数期望与状态根对象一起调用。

人为的例子:

/* Selectors */
const getTodos = state => state.todos;

const getUncompletedTodos = createSelector(
    [ getTodos ],
    todos => todos.filter(t => !t.completed)
);

/* Reducer */
const todosReducer = (state, action) => {
  switch (action.type) {
    case 'ADD_TODO':
      return [
        ...state,
        {
          id: action.id,
          text: action.text,
          completed: false
        }
      ];
    case 'REMOVE_COMPLETED_TODOS':
      return getUncompletedTodos(state); // <-- this won't work
  }
}

【问题讨论】:

    标签: redux reselect


    【解决方案1】:

    您的选择器从根状态对象工作。

    你可以伪造这个

     return getUncompletedTodos({todos: state});
    

    但恕我直言,一个更好的主意是重用过滤功能

    /* Selectors */
    const getTodos = state => state.todos;
    
    const filterCompleted = todos => todos.filter(t => !t.completed)
    
    const getUncompletedTodos = createSelector(
        [ getTodos ],
        filterCompleted
    );
    
    // inside reducer
    case 'REMOVE_COMPLETED_TODOS':
        return filterCompleted(state);
    

    【讨论】:

    • 您为什么不想利用 getUncompletedTodos-selector 中的记忆功能?
    • @user2602152 你正在调用reducer来改变状态。这里的记忆就像总是缺少缓存。
    • 您建议如何使用更复杂的选择器?假设我想访问特定待办事项中的特定项目(例如:getCurrentTodo、getTaskListInCurrentTodo)。在这种情况下,使用您的第二个建议将需要您调用这两个函数来访问该项目。换句话说,如果不使用您提到的“伪造”方法,您就不能重用选择器的组合?
    • @Daynil 很难说没有看到你的代码。一般来说,reselect 是一个简单的记忆,适用于以下情况:该函数随后使用相同的参数多次调用,并且“足够重”以超过 lib 添加的额外操作。如果您的案例符合标准,它可能会从记忆中受益。
    • 我认为这不是一个好主意。引用:“你正在调用 reducer 来改变状态。这里的记忆就像总是缺少缓存。”这不是真的。因为在我调用选择器的那一刻,尚未执行突变。所以我应该能够利用记忆。
    【解决方案2】:

    Yury 的答案有效,但没有利用记忆(参见 cmets)。如果你愿意,解决方案是只为它需要的状态片段编写选择器。

    选择器会变成:

    const getUncompletedTodos = createSelector(
        [todos => todos], // Not sure if there's a way to skip this redundancy and still take advantage of memoization with reselect.
        todos => todos.filter(t => !t.completed)
    );
    

    在reducer中,你可以像这样简单地使用它:

    case 'REMOVE_COMPLETED_TODOS':
        return getUncompletedTodos(state);
    

    但是,当在其他地方的根状态上使用选择器时,你可以这样使用它:

    getUncompletedTodos(state.todos)
    

    我看到的唯一缺点是您必须记住使用状态的正确部分调用选择器,当然如果您正确使用 TypeScript,它会提醒您这一点。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2020-01-30
      • 2016-10-06
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-09-14
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多