π§ Introduction
As applications grow, state shape becomes more important than the state itself.
Many Redux apps become slow and hard to maintain because:
- Data is deeply nested
- Same data is duplicated in multiple places
- Updating one item requires updating many arrays
This is not a Redux problem β
itβs a data modeling problem.
Professional Redux applications solve this using:
π Normalized State
π Redux Toolkit Entity Adapters
In this lesson, youβll learn how senior developers structure global state for scalability.
π― What Youβll Learn in This Lesson
By the end of this lesson, you will understand:
- What normalized state means
- Why nested state causes problems
- How entity adapters work
- How to CRUD data efficiently
- How this improves performance & maintainability
β What Is Normalized State?
Normalized state means:
Storing data in a flat structure, indexed by IDs.
Instead of nesting objects inside objects.
β Problem: Nested State (Anti-Pattern)
{
users: [
{
id: 1,
name: "Avni",
posts: [
{ id: 101, title: "Post A" },
{ id: 102, title: "Post B" }
]
}
]
}
Problems:
- Hard to update a single post
- Duplicate data risk
- Deep updates = complex logic
- Performance issues
β Solution: Normalized State
{
users: {
byId: {
1: { id: 1, name: "Avni", posts: [101, 102] }
},
allIds: [1]
},
posts: {
byId: {
101: { id: 101, title: "Post A" },
102: { id: 102, title: "Post B" }
},
allIds: [101, 102]
}
}
π Flat, predictable, scalable.
π§ Why Normalization Matters (Real Reasons)
Normalization gives you:
β
O(1) access by ID
β
No duplication
β
Easier updates
β
Better performance
β
Cleaner reducers
This matters a lot in large apps.
π§© Enter Redux Toolkit Entity Adapters
Redux Toolkit provides:
π createEntityAdapter
It:
- Enforces normalized structure
- Generates reducers automatically
- Generates selectors automatically
You donβt have to manually manage byId and allIds.
π§ What Is an Entity Adapter?
An entity adapter:
Is a helper that manages a collection of items in normalized form.
It assumes each entity has:
- A unique
id
π§© Creating an Entity Adapter
import { createEntityAdapter } from "@reduxjs/toolkit";
const usersAdapter = createEntityAdapter();
Optional customization:
const usersAdapter = createEntityAdapter({
selectId: user => user.userId,
sortComparer: (a, b) => a.name.localeCompare(b.name)
});
ποΈ Initial State with Adapter
const initialState = usersAdapter.getInitialState({
loading: false,
error: null
});
Adapter manages entities,
you manage extra state.
π§© Creating Slice with Adapter
const usersSlice = createSlice({
name: "users",
initialState,
reducers: {
addUser: usersAdapter.addOne,
addUsers: usersAdapter.addMany,
updateUser: usersAdapter.updateOne,
removeUser: usersAdapter.removeOne
}
});
π No reducer logic written manually.
π CRUD Operations Made Easy
| Operation | Method |
|---|---|
| Add one | addOne |
| Add many | addMany |
| Update | updateOne |
| Delete | removeOne |
| Replace all | setAll |
| Upsert | upsertOne |
Example:
dispatch(addUser({ id: 3, name: "Palak" }));
π Combining with createAsyncThunk
extraReducers: builder => {
builder
.addCase(fetchUsers.fulfilled, (state, action) => {
usersAdapter.setAll(state, action.payload);
state.loading = false;
});
}
π Perfect for API-driven apps.
π Selectors with Entity Adapter
Entity adapters generate selectors automatically.
export const {
selectAll,
selectById,
selectIds
} = usersAdapter.getSelectors(
state => state.users
);
Usage:
const users = useSelector(selectAll);
const user = useSelector(state => selectById(state, userId));
π§ Performance Benefits (Advanced Insight)
- No array looping for lookup
- No unnecessary re-renders
- Minimal updates
- Works perfectly with memoized selectors
This is production-grade state management.
π¨ Common Mistakes
β Mistake 1: Using Adapter for Small Data
Not needed for small lists.
β Mistake 2: Mixing Normalized & Nested Data
Pick one strategy.
β Mistake 3: Forgetting Extra State
Adapters donβt manage loading/error.
π§ When Should You Normalize State?
Normalize when:
β
Large collections
β
Frequent updates
β
Shared entities
β
Complex relationships
Donβt normalize when:
β Small, simple lists
β Short-lived data
π Entity Adapter vs Plain Redux
| Feature | Plain Redux | Entity Adapter |
|---|---|---|
| Boilerplate | High | Low |
| Performance | Manual | Optimized |
| Selectors | Manual | Auto-generated |
| CRUD logic | Manual | Built-in |
π― Best Practices (Senior-Level)
β
Normalize only when needed
β
Use adapters for collections
β
Keep slices feature-based
β
Combine with async thunks
β
Prefer clarity over premature optimization
β FAQs β Normalized State
πΉ Is normalization mandatory?
No, but essential for scale.
πΉ Does normalization improve performance?
Yes β significantly for large datasets.
πΉ Can I use adapters without Redux Toolkit?
No β theyβre RTK features.
πΉ Is this used in real apps?
Yes β extensively.
π§ Quick Recap
β Normalized state = flat structure
β Avoid deep nesting
β Entity adapters automate CRUD
β Selectors come for free
β Scales beautifully
π Conclusion
Normalized state is a turning point in your Redux journey.
Once you adopt it:
- State becomes predictable
- Updates become easy
- Performance improves naturally
This lesson moves you from Redux user to Redux architect βοΈπ§
π Next Lesson
Lesson 15 β Context vs Redux vs React Query (Choosing the Right Tool)