Movatterモバイル変換


[0]ホーム

URL:


Advisory boards aren’t only for executives.Join the LogRocket Content Advisory Board today
LogRocket blog logo
2021-10-18
1635
#react#redux
Kasra Khosravi
71706
Oct 18, 2021 ⋅ 5 min read

React re-reselect: Better memoization and cache management

Kasra KhosraviFounder atFeedbackOnSite.co.
LogRocket Galileo logo
Introducing Galileo AI
LogRocket’s Galileo AI watches every session, surfacing impactful user struggle and key behavior patterns.
LEARN MORE

See how LogRocket's Galileo AI surfaces the most severe issues for you

No signup required

Check it out

Although React’s components have a fast lifecycle, theysuffer due to excessive re-rendering, harming production time and performance overall. While in smaller applications, these unnecessary re-renders typically go unnoticed, as a UI grows with heavier components rendering on the component tree, both developers and users experience the negative side effects.

React ReReselect

To combat the problem, developers have introduced third-party performance optimization libraries likere-reselect, a wrapper for the popular libraryReselect, which is used mainly with Redux to enhance performance by writing memoized selectors. In this tutorial, we’ll explore re-reselect by creating a simple to-do list application.

To follow along with this tutorial, you should be familiar React and Reselect. Let’s get started!

🚀 Sign up for The Replay newsletter

The Replay is a weekly newsletter for dev and engineering leaders.

Delivered once a week, it's your curated guide to the most important conversations around frontend dev, emerging AI tools, and the state of modern software.

Selectors

Essentially, selectors are functions that take Redux state as an input and return a value derived from that state. In React Redux, when theuseSelector() Hook’s reducer action is dispatched, it runs for every mounted component, recalculating the selector with every dispatch and causing performance issues.

We often perform expensive calculations in selector functions. There are instances where our selector does not see any value change despite dispatching actions for the reducer or re-rendering the component. Ideally, the selector should return the same value without any calculations.

However, after a component re-renders in React, everything is created with a new reference. Therefore, the selector function has to perform the expensive calculation again.

Building our to-do list application

Let’s see an example of this with code; assume that we have a to-do list application with the central state in the reducer and the following folder structure:

React To Do App Folder Structure

We’ll install the following dependencies in our project:

React To Do App Dependencies

To create the application store and set the entry point element noderoot, add the following code toindex.js:

import React from 'react';import ReactDOM from 'react-dom';import './index.css';import { Provider } from 'react-redux';import { createStore } from 'redux';import rootReducer from './reducers/rootReducer';import App from './App';import * as serviceWorker from './serviceWorker';const store = createStore(rootReducer)ReactDOM.render(<Provider store={store}><App /></Provider>, document.getElementById('root'));serviceWorker.unregister();

Next, we’ll generate the main representational component for rendering our application’s layout. Add the code below toapp.js:

import React from 'react';import TodoList from './views/TodoList';function App() {   return (       <div className="App">           <main className="container">               <div className="divider"></div>               <TodoList/>           </main>       </div>   );}export default App;

todoReducer.js contains the central state for our application, which is extracted by the selector functions:

const initState = {   todoList: [       {           id: 1,           content: 'Play video game',           weight:1       }, {           id: 2,           content: 'Learn nodejs & python',           weight:2       }, {           id: 3,           content: 'Join meetup event',           weight:3       }   ]}const todoReducer = (state = initState, action) => {   switch (action.type) {       case 'ADD_TODO':           return {               ...state,               todoList: [                   ...state.todoList,                   action.payload               ]           }       case 'REMOVE_TODO':           return {               ...state,               todoList: action.payload           }       default:           return state   }}export default todoReducer;

InrootReducer.js, we’ll combine all of the reducers in the application:

import todoReducer from './todoReducer';import {combineReducers} from 'redux';//Combine all the sub reducersconst rootReducer = combineReducers({   todos:todoReducer})export default rootReducer

We have aTodoItem that renders the reducer’stodo state. Each item has a cross icon that will dispatch an event, deleting that specific item based on its ID:

import React from 'react';import {useSelector, useDispatch} from 'react-redux';//Single todo item componentconst TodoItem = (props) => {   const todoList = useSelector(state => state.todos.todoList)   const dispatch = useDispatch();   const removeTodoItem = (todoId) => {       let newTodoList = todoList.filter(item => item.id !== todoId);       dispatch({type: 'REMOVE_TODO', payload: newTodoList})   }   return (       <li className="collection-item" key={props.item.id}>{props.item.content}           <span               onClick={() => {               removeTodoItem(props.item.id)           }}               className="secondary-content">               <i className="remove-btn material-icons blue-text">clear</i>           </span>       </li>   );}export default TodoItem;

Finally, we’ll create a component calledTodoList.js, where we call our selectors along with a list of empty nodes, making the component extensive for the DOM:

import React,{useState} from 'react';import { useSelector,useDispatch } from 'react-redux';import TodoItem from '../components/TodoItem';const TodoList = () => { const todoList = useSelector(state => state.todos.todoList.filter(todo => todo.content.includes('a'))); const dispatch = useDispatch(); const [inputTodo,setInputTodo] =  useState(''); const handleInput = (e)=>{   setInputTodo(e.target.value); } //Handle onClick event const addNewTodo = ()=>{   //Valid input value        let newTodoObject={           id: Math.random(),           content:inputTodo,           weight:Math.random(),       }        //Add new todo item into List with the action       dispatch({type:'ADD_TODO',payload:newTodoObject});       setInputTodo('');         function returnBlankNodes(){           let nodes = [];                   for (let i=0;i<10000;i++) {                   nodes.push(<p></p>)   }                   return nodes } }   return (          <section>       <h3 className="center-align white-text blue">Todo List</h3>       {           todoList.length>0?           (<ul className="collection">           {             todoList.map(item => {               return <TodoItem key={item.id} item={item} />             })           }         </ul>):         (<p className="center-align">You don't have anything to do! Awesome!</p>)       }       <div className="row">       <p className="red-text err-msg col s12 center-align">       </p>       <div className="input-field col s10">       <input onChange={handleInput} value={inputTodo} placeholder="Add todo..." type="text" />       <label htmlFor="todo-input" className="active">New Todo</label>       </div>       <button className="btn col s2 blue" onClick={addNewTodo} >Add</button>       </div>       {returnBlankNodes()}     </section>     );}export default TodoList;

We are taking thetodoList from our store and rendering the component on the browser. The list is then extracted by thetodoList selector with filtering applied to it.

Currently, thetodoList is recalculated with each render. Anytime we enter anything in our text field, the state of our component changes. Ideally, the selector should run only when we make a change in the Redux store, for example, when we add a new to-do item.

If there are hundreds of items in our Redux store, thetodoList selector will be CPU extensive. In these cases, we have to use memoization, which will run the selector only when the value oftodoList changes in our store.

Why re-reselect?

We can use re-reselect to create memoized selectors. The result will be stored in the memory, and it will be fetched only if the value doesn’t change. Each selector has its own memoization, meaning it cannot cache multiple selectors.

Reselect example

To better understand this, let’s consider another following example:

import { createSelector } from 'reselect'const getTodosSelector = state => state.todos.todoListconst getTodosWithLetter = createSelector(   getTodosSelector,   (state,Letter)=>Letter,   (toDos,Letter) => toDos.filter(todo.content.includes(Letter)))const a=getTodosWithLetter(state,’a’) //cache createdconst e=getTodosWithLetter(state,’e’)  //cache invalidatedconst a_again=getTodosWithLetter(state,’a’) //cache created again.a!===a_again.

In the code snippet above, we created a selector using Reselect that takes an alphabet and filters the results based on it. Currently, one selector is shared between all of the English letters, however, the selector can have a cache for only a single letter.

We could create selectors for each individual letter, however, this approach is tedious. As we can see above, we havea,e, anda_again. At first, the selector creates a cache for lettera, but after providing a different letter, the cache is invalidated and a new cache is created for the lettere. Ideally, creating a selector with a different letter should add a new cache without overriding the previous one.

re-reselect example

Instead, let’s re-write the code above with re-reselect as follows:



import {createCachedSelector} from 're-reselect';const getTodosSelector = state => state.todos.todoListconst getTodosWithLetter = createCachedSelector(   getTodosSelector,   (state,Letter)=>Letter,   (toDos,Letter) => toDos.filter(todo.content.includes(Letter)))( (state, Letter) => Letter // Cache selectors by Letter name);const a=getTodosWithLetter(state,'a') //cache createdconst e=getTodosWithLetter(state,'e')  //new cache createdconst a_again=getTodosWithLetter(state,'a') //fetched from cache// memoization is preserved among different components

To use the code above, we must simply importcreateCachedSelector. Notice that the syntax is almost similar to Reselect.

Additionally, we must tell the selector about the cache criteria. In the code above, we have specified it with the letter name. Instead of destroying the previous cache, a new cache is created for a single selector.

Reselect is already very performant, but re-reselect takes things one step further by introducing deep cache management. In our example above, we have created three selectors.

When we used Reselect earlier, our filter ran three times because new caches were constantly recreated. however, with re-reselect, our filter ran only two times. Witha_again, it fetches the data from our cache against the letter.

Add re-reselect to our to-do list application

To integrate re-reselect with React Redux, let’s create a new file calledtodoSelector.js and add the following code:

// todoSelector.jsimport {createCachedSelector} from 're-reselect';export const getTodosSelector = state => state.todos.todoListexport const  getTodosWithLetter = createCachedSelector(    getTodosSelector,    (state,Letter)=>Letter,    (todoList,Letter) => todoList.filter(todo=>todo.content.includes(Letter)) )(  (state, Letter) => Letter // Cache selectors by Letter name );

Import it inTodoList.js as follows:

import { getTodosWithLetter } from './todoSelector';const todoList = useSelector(state=>getTodosWithLetter(state,'a'));

Output

Let’s run the project in the browser to see the result. Start the project by running the following command:

yarn startTo Do App Gif Final Project

Voila, we’ve successfully added a cached selector with re-reselect!

Conclusion

In this article, we learned how to create selectors that have deeply managed caches. We built an example to-do list application with a component that recalculated on each re-render. After integrating re-reselect, the selector will only run when we make a change in the Redux store.

If you have expensive selectors in your project, re-reselect can drastically improve your app’s performance, especially if you have a larger application. I hope you enjoyed this tutorial.

Get set up with LogRocket's modern React error tracking in minutes:

  1. Visithttps://logrocket.com/signup/ to get an app ID
  2. Install LogRocket via npm or script tag.LogRocket.init() must be called client-side, not server-side

    $ npm i --save logrocket // Code:import LogRocket from 'logrocket'; LogRocket.init('app/id');
    // Add to your HTML:<script src="https://cdn.lr-ingest.com/LogRocket.min.js"></script><script>window.LogRocket && window.LogRocket.init('app/id');</script>
  3. (Optional) Install plugins for deeper integrations with your stack:
    • Redux middleware
    • NgRx middleware
    • Vuex plugin
Get started now

Stop guessing about your digital experience with LogRocket

Get started for free

Recent posts:

the replay graphic november 5

The Replay (11/5/25): Developer elitism, REST APIs, and more

Discover what’s new in The Replay, LogRocket’s newsletter for dev and engineering leaders, in the November 5th issue.

Matt MacCormack
Nov 5, 2025 ⋅ 32 sec read
lewis cianci quote developer elitism

It’s time to break the cycle of developer elitism

A senior developer discusses how developer elitism breeds contempt and over-reliance on AI, and how you can avoid it in your own workplace.

Lewis Cianci
Nov 5, 2025 ⋅ 13 min read
open ai agent kit

I tried OpenAI’s AgentKit: Does it make Zapier and n8n obsolete?

Examine AgentKit, Open AI’s new tool for building agents. Conduct a side-by-side comparison with n8n by building AI agents with each tool.

Clara Ekekenta
Nov 4, 2025 ⋅ 11 min read

A Jarvis for everyone: AI agents as new interfaces

AI agents powered by MCP are redefining interfaces, shifting from clicks to intelligent, context-aware conversations.

Peter Aideloje
Nov 4, 2025 ⋅ 10 min read
View all posts

One Reply to "React re-reselect: Better memoization and cache management"

  1. Hi,
    Thank you for your post.
    Also checkhttps://github.com/sgrishchenko/reselect-utils

Leave a ReplyCancel reply

Hey there, want to help make our blog better?

Join LogRocket’s Content Advisory Board. You’ll help inform the type of content we create and get access to exclusive meetups, social accreditation, and swag.

Sign up now
 

Loading Comments...
 


    [8]ページ先頭

    ©2009-2025 Movatter.jp