# Common Reselect Gotchas
source: https://developer.mastercard.com/blog/common-reselect-gotchas/index.md

Authors: Andrew Freix. Reselect is a memoization library often used with Redux . If you're not familiar with the basics of these libraries, I'd read the introductory docs before returning to this post. Despite Reselects popularity, its nuances can be easily be misinterpreted, which can lead to losing out on the performance benefits Reselect is intended to provide. In this post we'll give a brief overview of Reselect and why it's useful, with a focus on its usage with React and Redux. If you're already familiar with the basics, you can skip right ahead to the Gotchas section. Why Reselect? With Redux you may have many different components connected to state in different ways. In general, its best practice to store your app's state in a normalized manner . You'll often have components that need to display the same underlying data in slightly different ways. Additionally, you may want to filter or sort the data before it's displayed to the user. In other words, you'll compute derived data from your normalized Redux store (aka state). While computing derived data may not be that expensive, by default, mapStateToProps is called for all connected components any time state changes. This could lead to recomputing all derived data for your entire app whenever state changes, which is where Reselect comes in. It allows you to memoize your selectors so that you only need to recompute derived data when relevant slices of state change. Basics of Reselect Reselect // State { todos: \[ { id: 1, text: 'one', isCompleted: false}, { id: 2, text: 'two', isCompleted: true}, { id: 3, text: 'three', isCompleted: false}\] } getTodos = state =\> state.todos getCompletedTodos = createSelector( \[getTodos\], getCompletedTodosInternal ) getCompletedTodosInternal = (todos) =\> { return todos.filter(todo =\> todo.isCompleted) } getSortedCompletedTodos = createSelector( \[getCompletedTodos\], todos =\> { return todos.sort(todoSorter); } function todoSorter(a, b) { // Implement } As you can see from above, the createSelector takes in an array of functions, which we'll call input functions, whose return values are then passed as parameters to the final output function. Selectors are composable, which means that you can use memoized selectors as inputs to other memoized selectors, as shown in getSortedCompletedTodos. Memoization Reselect will memoize the result of the output function if the results of the input functions do not change. It's important to understand that, by default, Reselect uses a shallow reference comparison to determine equality, which is a common theme throughout the React stack (React's PureComponents use a shallow comparison when determining whether or not to re-render). It's also important to know that memoized selectors have a cache size of 1. For example, if you call getSortedCompletedTodos twice in a row without state.todos changing, then the output function, getCompletedTodosInternal , won't be called. Instead Reselect will return the memoized result. Below is an example mapStateToProps function. Reselect function mapStateToProps(state) { return { sortedCompletedTodos: getSortedCompletedTodos(state); } } Any time an action is dispatched that doesn't change state.todos we won't have to recompute the list of sorted todos, and that is the key benefit of Reselect. Gotchas While at its core Reselect is a fairly simple library, I often see developers making a few common mistakes: Putting computation in input selectors Returning new references in input selectors Passing new object references as additional arguments to selectors Putting Computation in Input Selectors Reselect // State { cartItems: \[{id: 1, price: 4}, {id: 2, price: 6}\], tax: 0.05, } // getSubTotal will recompute any time state changes const getSubTotal = (state) =\> { return state.cartItems.reduce((subTotal, item) =\> { return (subTotal + item.price) }, 0) } // O(1) operation const getTax = (state) =\> state.tax; const getTotal = createSelector(\[getSubTotal, getTax\], (subTotal, tax) =\> return subTotal\*tax ) ////////////////////////////////////////////////////// // O(1) operation const getCartItems = (state) =\> state.cartItems // Do "expensive" operations within the output selector const getTotal = createSelector(\[getCartItems, getTax\], (cartItems, tax) =\> (tax \* cartItems.reduce((subTotal, item) =\> { return (subTotal + item.price) }, 0)) ) Returning new references in input selectors I will give two examples. The first is a trivial example of this situation which is easier to fix. The second is a more realistic example of when this problem may occur. Using the same todo state as previous examples. Reselect // Two problems: // - we are doing "expensive" work in an input selector // - we are returning a new reference each time getCompletedTodos = state =\> state.todos .filter(todo =\> todo.isCompleted) getSortedCompletedTodos = createSelector( \[getCompletedTodos\], todos =\> { return todos.sort(todoSorter); } function todoSorter(a, b) { // Implement } In the example above, any time state changes, getCompletedTodos will recompute, which returns a new array reference. Since getCompletedTodos is used as an input selector to getSortedCompletedTodos then getSortedCompletedTodos will also recompute. You may be thinking this is eerily similar to the code I wrote up above in the Basics of Reselect section. The only difference is that getCompletedTodos was broken up and memoized. Reselect getTodos = state =\> state.todos getCompletedTodos = createSelector( \[getTodos\], todos =\> todos.filter(todo =\> todo.isCompleted) ) Aren't we returning a new reference in this code as well? You're correct, but it is okay since getCompletedTodos is memoized. If state.todos doesn't change then our memoized selector getCompletedTodos will return its cached result, which will be the same reference as before, meaning it's safe to use as an input selector. If state.todos does change then it makes sense that we'd have to recompute both getCompletedTodos and getSortedCompletedTodos. The previous example is a bit contrived, but when you're working with a complex application it can be harder to spot issues like this. Reselect // State { items: { byId: { 1: { id: 1, price: 3}, 2: {id: 2, price: 5}, 3: {id: 2, price: 8} } ids: \[1, 2, 3\] }, carts: { byId: { 1: { cartId: 1, itemIds: \[1, 2\] }, 2: { cartId: 2, itemIds: \[2, 3\] } } ids: \[1, 2\] } }; const getItemMap = state =\> state.items.byId; // You can pass arguments to selectors as well const getCart = (state, cartId) =\> state.carts.byId\[cartId\]; const getCartTotal = createSelector( \[getItemMap, getCart\], (itemMap, cart) =\> { return cart.itemIds.reduce((total, itemId) =\> { return total + itemMap.get(itemId).price; }, 0); } ); // Example usage const total = getCartTotal(state, cartId); The example above shows a common state shape. getCarTotal is intended to return the total price of all items in a given cart, and one of the input selectors to getCartTotal is getItemMap. Assuming any change to an item also changes the reference returned by getItemMap, which should be the case, then any time any item in our Redux state changes getCartTotal will recompute. This will happen regardless of whether the items in the cart actually changed. Whether you care is a different story. Perhaps the clean code justifies this performance cost. Redux and Reselect are opinionated when it comes to the design of your store and the structure of your selectors. It's important to understand the tools so that you're able to design performant code that fits your use case. There is one last addendum that is important to call out. Often times, Reselect is used with Immutable.js . Immutable.js provides immutable data structures that by default return new references for any operation. If you are using Immutable.js in many of your input selectors, you may be missing out on Reselect's memoization benefits. Example: Reselect // Immutable State { todos: \[ {id: 1, text: 'I am in an Immutable List', isCompleted: false} \] } // Convert from Immutable List to a JS array, which returns a new // reference const getTodos = state =\> state.todos.toJS(); const getCompletedTodos = createSelector( \[getTodos\], todos =\> todos.filter(todo =\> todo.isCompleted) ) I hope you enjoyed this post! I plan on writing a second post that talks about the third gotcha that can come up when passing props to your selectors.
