With Parameters
Filter the todo list with a needle parameter.
const selectTodos = createSelector(
[
(state: RootState) => state.todos,
(_, needle: string) => needle,
],
(todos, needle) => todos.filter(todo => todo.text.includes(needle))
)
// Usage in a component
const TodoList = ({searchNeedle}) => {
const todos = useSelector(state => selectTodos(state, searchNeedle));
return todos.map(todo => (
<Todo key={todo.id} todo={todo} />
);
}
If you don’t like this syntax, see the Reselect FAQ on how to create a curried selector instead.
// This would allow you to do this instead:
const todos = useSelector(selectTodos(searchNeedle));
// The FAQ also contains a recipe to turn this into:
const todos = useSelectTodos(searchNeedle);
Multiple Parameters
const selectTodos = createSelector(
[
(state: RootState) => state.todos,
(_, needle: string) => needle,
(_, __, caseSensitive: boolean) => caseSensitive
],
(todos, needle, caseSensitive) => { /* magic here */ }
)
createSelectorOptions
Reselect will only execute the combiner fn when the inputs have changed, which is typically your entire root state (with potentially additional parameters) AND the result of those input selectors has changed.

Configure this behavior with argsMemoize(Options) and memoize(Options).
If you want to change the default behavior of many/all selectors, use createSelectorCreator.
lruMemoize
lruMemoize (Least Recently Used) was the default before v5 of Reselect. It had a default cache size of 1, meaning a recomputation every time one of the inputSelectors changed.
The “problem” with lruMemoize was that a cache of size 1 might not be that useful! This was typically solved with useMemo or by passing memoizeOptions: { maxSize: 5 }.
import { lruMemoize } from "reselect";
import { shallowEqual } from "react-redux";
export const selectMyTodosCompletedCount = createSelector(
[(state: RootState) => ({todos: state.todos, userId: state.more.users.ids[0]})],
({todos, userId}) => todos.filter(todo => todo.userId === userId && todo.done).length,
{
memoize: lruMemoize,
memoizeOptions: {
equalityCheck: (a, b) => a.todos === b.todos && a.userId === b.userId,
// resultEqualityCheck: shallowEqual,
// maxSize: 10
},
// argsMemoize: lruMemoize,
// argsMemoizeOptions: {
// equalityCheck: shallowEqual,
// resultEqualityCheck: shallowEqual,
// maxSize: 10
// }
}
)
weakMapMemoize (default)
weakMapMemoize is the new default since v5 and provides a dynamic cache size out of the box.
unstable_autotrackMemoize (experimental)
There is also the experimental unstable_autotrackMemoize, which, like proxy-memoize below, can be more efficient.
import { unstable_autotrackMemoize } from "reselect";
// The combiner (todos.filter) will NOT execute when we
// toggle a Todo between done/not done as that field is
// not accessed.
const selectTodos = createSelector(
[
(state: RootState) => state.todos,
(_, needle: string) => needle,
],
(todos, needle) => todos.filter(todo => todo.text.includes(needle)),
{
memoize: unstable_autotrackMemoize,
}
)
createSlice
Common selectors can already be defined in the createSlice setup.
const todoSlice = createSlice({
initialState: [],
// Predefined selectors, which receive the slice's state as their first parameter.
selectors: {
selectTodo: (state, id: number): Todo | undefined => {
return state.find(todo => todo.id === id);
},
},
});
export const { selectTodo } = todoSlice.selectors;
// Usage in a component
const todo = useSelector(state => selectTodo(state, todoId));
TypeScript
See ReduxJS/Toolkit: TypeScript if you’re unsure how to create the RootState.
import { createSelector } from "@reduxjs/toolkit";
const createAppSelector = createSelector.withTypes<RootState>();
Debugging
skortchmark9/reselect-tools: Debugging Tools for Reselect
Once you get many selectors depending on other selectors, you can use this tool to visualize re-computations.
If your debugging needs are basic, you can check the Output Selector Fields:
const meta = {
lastResult: selectMyTodosCompletedCount.lastResult(),
recomputations: selectMyTodosCompletedCount.recomputations(),
dependencyRecomputations: selectMyTodosCompletedCount.dependencyRecomputations(),
}
Alternatives
proxy-memoize
dai-shi/proxy-memoize: Intuitive magical memoization library with Proxy and WeakMap
This will re-execute when the done property toggles even though it has no impact on the actual result.
import { createSelector } from "reselect";
const selectTodoDescriptionsReselect = createSelector(
[state => state.todos],
todos => todos.map(todo => todo.text)
)
With proxy-memoize, this would not be the case as due to the proxy, it figures only text is actually relevant for selector.
import { memoize } from "proxy-memoize";
const selectTodoDescriptionsProxy = memoize(state =>
state.todos.map(todo => todo.text)
)
For this reason the ReduxJS/Toolkit officially encourages you to consider using proxy-memoize as a viable alternative to Reselect.
re-reselect
toomuchdesign/re-reselect: Enhance Reselect selectors with deeper memoization and cache management.
As of Reselect v5, what you’d need this library for can now achieved with Reselect createSelectorOptions natively.