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
Are there any more questions? Ask them in the Discussion section.
The Hexlet support team or other students will answer you.
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.