Most applications work with data that has a nested structure. For example, blog posts have an author and comments. Comments also have authors and can have likes.
const blogPosts = [
{
id: 'post1',
author: { username: 'user1', name: 'User 1' },
body: '......',
comments: [
{
id: 'comment1',
author: { username: 'user2', name: 'User 2' },
comment: '.....'
},
{
id: 'comment2',
author: { username: 'user3', name: 'User 3' },
comment: '.....'
}
]
},
];
Working directly with such a structure is difficult for several reasons:
The correct approach when working with Redux is to think of it as a relational database. The data inside the store must be normalized. In this way, each slice working with a set of entities can be seen as a separate table in the database.
The basic principles of organizing data in the store:
{
posts: {
entities: {
post1: {
id: 'post1',
author: 'user1',
body: '......',
comments: ['comment1', 'comment2'],
},
post2: {
id: 'post2',
author: 'user2',
body: '......',
comments: [],
},
},
ids: ['post1', 'post2'],
},
comments: {
entities: {
comment1: {
id: 'comment1',
author: 'user2',
comment: '.....',
},
comment2: {
id: 'comment2',
author: 'user3',
comment: '.....',
},
},
ids: ['comment1', 'comment2'],
},
users: {
entities: {
user1: {
id: 'user1',
username: 'user1',
name: 'User 1',
},
user2: {
id: 'user2',
username: 'user2',
name: 'User 2',
},
user3: {
id: 'user3',
username: 'user3',
name: 'User 3',
},
},
ids: ['user1', 'user2', 'user3'],
}
}
The data are now normalized. Each entity is stored in its own reducer. The entities object stores the entities
themselves and the ids
store the identifiers. What advantages have we gained?
Now let's see what it looks like inside the slices:
const slice = createSlice({
name: 'users',
initialState: {
ids: [],
entities: {},
},
reducers: {
addUser(state, action) {
const { user } = action.payload;
state.entities[user.id] = user;
state.ids.push(user.id);
},
removeUser(state, action) {
const { userId } = action.payload;
delete state.entities[userId];
state.ids = state.ids.filter((id) => id !== userId);
},
updateUser(state, action) {
const { userId, data } = action.payload;
Object.assign(state.entities[userId], data);
}
},
});
dispatch(addUser({ user }));
dispatch(removeUser({ userId }));
dispatch(updateUser({ userId, data }));
The data that come from the backend isn't usually normalized, as it's convenient for the frontend. Therefore, before adding them to the store, you must first carry out normalization. This can be done either by manually traversing the collection and converting it to the desired object, or by using the normalizr library.
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.