Memoization optimizes costly function calls by caching results for future identical inputs. Reselect, a library for Redux applications, provides the creation of memoized selectors. In this article, we will learn how to create memoized selectors using the Reselect library.
What is Reselect?Reselect is a simple selector library for Redux that allows you to create memoized selectors. A selector is a function that takes the state as an argument and returns a derived piece of state. By memoizing these selectors, Reselect ensures that they are only recalculated when their input arguments change, preventing unnecessary re-renders.
Approaches Reselect offers a few different ways to create selectors. The most common approach involves using the createSelector function.
- Basic Selector: A basic selector is a simple function that extracts a specific piece of state:
- Memoized Selector: A memoized selector uses createSelector to memoize the result of a computation:
- Complex Memoized Selector: You can combine multiple selectors to create more complex memoized selectors:
Basic Selector A basic selector directly extracts a portion of the state from the Redux store
Example: This function takes the state as an argument and returns the userList array.
const getUsers = (state) => state.users.userList; Memoized Selector A memoized selector uses the createSelector function to cache the results of a computation. This function takes an array of input selectors and a transform function:
Example: This Illustrates getUserNames is a memoized selector that maps over the userList and extracts user names.
import { createSelector } from 'reselect';
const getUserNames = createSelector( [getUsers], (userList) => userList.map(user => ({ id: user.id, name: user.name, isActive: user.isActive })) ); Complex Memoized Selector More complex memoized selectors can combine multiple input selectors.
Example: This selector counts the number of active users by filtering the userList.
const getActiveUsersCount = createSelector( [getUsers], (userList) => userList.filter(user => user.isActive).length ); Steps to Create Application (Install Required Modules) .To create a React application that uses Reselect, follow these steps:
Step 1: Create a React Application named ‘memoized’ and navigate to it using this command.
npx create-react-app memoized cd memoized Step 2: Install required packages and dependencies.
npm install redux react-redux reselect Project Structure: Project Structure Updated Dependencies in package.json File. "dependencies": { "@testing-library/jest-dom": "^5.17.0", "@testing-library/react": "^13.4.0", "@testing-library/user-event": "^13.5.0", "react": "^18.2.0", "react-dom": "^18.2.0", "react-redux": "^8.1.3", "react-scripts": "5.0.1", "redux": "^4.2.1", "reselect": "^4.1.5", "web-vitals": "^2.1.4" }
Example: This example shows the implementation of the above-explained approach
CSS
/* src/App.css */
body {
font-family: Arial, sans-serif;
margin: 0;
padding: 20px;
}
h1 {
color: #333;
}
h2 {
color: #555;
}
ul {
list-style-type: none;
padding: 0;
}
li {
background-color: #f0f0f0;
margin: 5px 0;
padding: 10px;
border-radius: 4px;
display: flex;
justify-content: space-between;
align-items: center;
}
button {
padding: 5px 10px;
background-color: #007bff;
color: white;
border: none;
border-radius: 4px;
cursor: pointer;
}
button:hover {
background-color: #0056b3;
}
p {
font-weight: bold;
}
JavaScript
//src/components/UserList.js
import React from 'react';
import { useSelector, useDispatch } from 'react-redux';
import { getUserNames, getActiveUsersCount } from '../selectors/userSelectors';
import { toggleUserActive } from '../actions/userActions';
const UserList = () => {
const userNames = useSelector(getUserNames);
const activeUsersCount = useSelector(getActiveUsersCount);
const dispatch = useDispatch();
const handleToggleActive = (userId) => {
dispatch(toggleUserActive(userId));
};
return (
<div>
<h2>User List</h2>
<ul>
{userNames.map(user => (
<li key={user.id}>
{user.name}
<button onClick={() => handleToggleActive(user.id)}>
{user.isActive ? 'Deactivate' : 'Activate'}
</button>
</li>
))}
</ul>
<p>Active Users Count: {activeUsersCount}</p>
</div>
);
};
export default UserList;
JavaScript
//src/store.js
import { createStore } from 'redux';
import rootReducer from './reducers';
const store = createStore(rootReducer);
export default store;
JavaScript
//src/actions/userActions.js
export const setUserList = (users) => ({
type: 'SET_USER_LIST',
payload: users
});
export const toggleUserActive = (userId) => ({
type: 'TOGGLE_USER_ACTIVE',
payload: userId
});
JavaScript
//src/reducers/index.js
import { combineReducers } from 'redux';
import usersReducer from './userReducer';
const rootReducer = combineReducers({
users: usersReducer,
// Add other reducers as needed
});
export default rootReducer;
JavaScript
//src/reducers/userReducer.js
const initialState = {
userList: []
};
const usersReducer = (state = initialState, action) => {
switch (action.type) {
case 'SET_USER_LIST':
return {
...state,
userList: action.payload.map(user => ({
...user,
isActive: false // Initialize isActive status as false
}))
};
case 'TOGGLE_USER_ACTIVE':
return {
...state,
userList: state.userList.map(user =>
user.id === action.payload ? { ...user, isActive: !user.isActive } : user
)
};
default:
return state;
}
};
export default usersReducer;
JavaScript
//src/selectors/userSelectors.js
import { createSelector } from 'reselect';
const getUsers = (state) => state.users.userList;
export const getUserNames = createSelector(
getUsers,
(userList) => userList.map(user =>
({ id: user.id, name: user.name, isActive: user.isActive }))
);
export const getActiveUsersCount = createSelector(
getUsers,
(userList) => userList.filter(user => user.isActive).length
);
JavaScript
//src/index.js
import React from 'react';
import ReactDOM from 'react-dom';
import { Provider } from 'react-redux';
import store from './store';
import App from './App';
import './App.css';
ReactDOM.render(
<Provider store={store}>
<App />
</Provider>,
document.getElementById('root')
);
JavaScript
//src/App.js
import React, { useEffect } from 'react';
import { useDispatch } from 'react-redux';
import { setUserList } from './actions/userActions';
import UserList from './components/UserList';
import './App.css';
const App = () => {
const dispatch = useDispatch();
useEffect(() => {
const fetchUsers = async () => {
const response = await fetch('https://jsonplaceholder.typicode.com/users');
const data = await response.json();
dispatch(setUserList(data));
};
fetchUsers();
}, [dispatch]);
return (
<div className="App">
<h1>Redux App with Reselect</h1>
<UserList />
</div>
);
};
export default App;
Output :Here’s a demonstrating the output of the above application:
|