A big part of any web application comes down to operations on entities, adding, changing, deleting, and reading them. For example, on Hexlet, in every lesson, there is a discussion section where questions can be asked. This part of the frontend 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);
};
Exactly the same code will be used 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 work properly
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 4 lines in the reducers, and it brought us a full implementation of standard operations on the user. But that's 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 users slice, 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
// 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)
– returns ids
selectEntities(state)
– returns entities
selectTotal(state)
– returns entitiesselectById(state, id)
– returns a specific entity or undefined
if nothing is found
// id – some kind of identifier
const user = useSelector((state) => selectors.selectById(state, id));
The Hexlet support team or other students will answer you.
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.
Programming courses for beginners and experienced developers. Start training for free
Our graduates work in companies:
From a novice to a developer. Get a job or your money back!
Sign up or sign in
Ask questions if you want to discuss a theory or an exercise. Hexlet Support Team and experienced community members can help find answers and solve a problem.