Lesson 14 β€” Normalized State & Entity Adapters (Redux Toolkit)

🧭 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

OperationMethod
Add oneaddOne
Add manyaddMany
UpdateupdateOne
DeleteremoveOne
Replace allsetAll
UpsertupsertOne

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

FeaturePlain ReduxEntity Adapter
BoilerplateHighLow
PerformanceManualOptimized
SelectorsManualAuto-generated
CRUD logicManualBuilt-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)

Leave a Comment