A big part of any web application comes down to operations on entities, adding, changing, deleting, and reading them. For example, in every Hexlet lesson, you can find a discussion section and ask some questions. This section of the front end handles posts, comments, authors, and likes. Thanks to data normalization, the code for processing these entities looks identical:
const addPost = (state, post) => {
state.entities[post.id] = post;
state.ids.push(post.id);
};
const addLike = (state, like) => {
state.entities[like.id] = like;
state.ids.push(like.id);
};
We will use the same code for all other entities. Is there any way to reuse it? Of course! Redux Toolkit does this using the Entity Adapter mechanism. It provides a set of ready-made reducers and selectors for basic entity operations. First, an example:
import {
createSlice,
createEntityAdapter,
} from '@reduxjs/toolkit';
const usersAdapter = createEntityAdapter();
// By default: { ids: [], entities: {} }
const initialState = usersAdapter.getInitialState();
const slice = createSlice({
name: 'users',
initialState,
reducers: {
addUser: usersAdapter.addOne,
addUsers: usersAdapter.addMany,
removeUser: usersAdapter.removeOne,
updateUser: usersAdapter.updateOne,
},
});
// Somewhere in the application
// By convention, the passed data must have an `id` in it to properly work
dispatch(addUser(user));
// The data are transmitted in this format: { id, changes }
dispatch(updateUser({ id: user.id, changes: data }));
// It's enough to pass the identifier
dispatch(removeUser(user.id));
Just four lines in the reducers brought us a full implementation of standard operations on the user. But that is not all. Besides ready-made reducers, Entity Adapter gives us a set of ready-made selectors to retrieve data from the store. To do this, they must be generated and exported from the file with the slice:
// file: usersSlice.js
// Callback defines a basic selector that extracts the desired part of the state from Redux
// For the `usersSlice`, it's `state.users`
export const selectors = usersAdapter.getSelectors((state) => state.users);
Example of its use in the application:
import { useSelector, useDispatch } from 'react-redux';
import { selectors } from '../slices/usersSlice.js';
const MyComponent = (props) => {
// Retrieving all users as an array
// The data from `state.users.entities` is selected internally
// And it is sorted by `state.users.ids`
const users = useSelector(selectors.selectAll);
// Here's the logic of the output
}
In addition to the selectAll(state)
we get:
selectIds(state)
– returnsids
selectEntities(state)
– returnsentities
selectTotal(state)
– returns the total amountselectById(state, id)
– returns a specific entity orundefined
if it found nothing// The `id` is some identifier const user = useSelector((state) => selectors.selectById(state, id));
Recommended materials
For full access to the course you need a professional subscription.
A professional subscription will give you full access to all Hexlet courses, projects and lifetime access to the theory of lessons learned. You can cancel your subscription at any time.