How to use Reselect selectors inside a Redux reducer

Viewed 13004

My app already has a large collection of selectors used by the various container objects. These are great for accessing different parts of the state and make refactoring the state much easier.

Now I want to use my selectors inside some of my reducer functions. The problem is that inside the reducer, the state parameter refers to a specific slice of the state, whereas the selector functions expect to be called with the state root object.

Contrived Example:

/* 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
  }
}
2 Answers

The answer by Yury works, but doesn't take advantage of memoization (see comments). If you want that, the solution would be to write the selector only for the slice of the state that it needs.

The selector would become:

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)
);

In the reducer, you would simply use it like this:

case 'REMOVE_COMPLETED_TODOS':
    return getUncompletedTodos(state);

However, when using the selector on the root state somewhere else, you use it like this:

getUncompletedTodos(state.todos)

The only downside I see is that you would have to remember to call the selector with the right part of the state, though of course if you're using TypeScript properly, it will remind you of this.

Related