Memoizing Selectors: Avoid Needless Re-renders
Memoized selectors prevent needless re-renders by caching derived data. A selector for a filtered list will only re-run its logic if the original list or filter criteria change, not on every state update.
WHY IT EXISTS: In state management patterns like Redux, any update to the global state tree can cause connected components to re-render. If a component derives data (e.g., filtering a list), it might re-calculate that data on every single render, even if the parts of the state it cares about haven't changed. This is inefficient and can lead to performance bottlenecks.
THE MENTAL MODEL: Think of a memoized selector as a specialized cache for a function's result. The function is your data transformation (e.g., filtering todos). The cache key is the function's inputs (e.g., the full todo list and the current filter). If the inputs are the same as the last time it was called, it returns the cached result instantly without re-running the transformation. It trades a small amount of memory for a big gain in computational efficiency.
HOW IT WORKS: Libraries like Reselect provide a createSelector function. You provide it with two things: first, one or more "input selectors" that extract specific slices of state (like state.todos). Second, a "result function" that takes the output of the input selectors and performs the final computation. createSelector returns a new, memoized selector. When you call this new selector, it first runs the input selectors and compares their results to the previous results. If they are strictly equal (===), it returns the last cached result. Only if an input has changed does it execute the result function to compute a new value, which it then caches for next time.
WHEN TO USE IT: Use memoized selectors whenever you compute derived data from your state store, especially for operations that are computationally expensive or create new array/object references. Common use cases include: filtering a list of items, sorting a list, or aggregating data (like calculating a total price from items in a cart). This is a standard pattern for optimizing performance in applications using Redux.
WHEN NOT TO USE IT: Avoid selectors for trivial data access that doesn't involve computation or derivation, like simply retrieving a user's name (state => state.user.name). The overhead of memoization isn't necessary. Also, if the input data is guaranteed to change on every call in a way that provides no opportunity for caching, memoization offers no benefit.
ONE CANONICAL EXAMPLE: Imagine you want to display completed todos. Instead of filtering inside your component, you create a selector. First, input selectors grab the raw data: const selectTodos = state => state.todos;. Then, the memoized selector combines them: const selectCompletedTodos = createSelector([selectTodos], todos => todos.filter(todo => todo.completed));. The expensive .filter() operation will only re-run if the state.todos array reference changes. If other state like state.user updates, the selector returns the cached result, preventing a re-render.
Read the original → reselect.js.org
Get five bites like this every day.
Tezvyn delivers a daily feed of 60-second tech bites with quizzes to lock in what you learn.