SkillByAIOpen interactive version →

Lesson 13 / 25

Normalising Data with createEntityAdapter

ids plus a lookup table.

Store each item once

Nested or duplicated data is hard to update consistently. Normalised state keeps each collection as { ids: [], entities: {} }: an ordered array of IDs and an object mapping ID to item, with relations stored as IDs. createEntityAdapter produces this shape with getInitialState(), provides CRUD reducer helpers (addOne, addMany, setAll, upsertMany, updateOne, removeOne and more) usable directly as case reducers or inside them, and generates selectors (selectAll, selectById, selectIds, selectEntities, selectTotal). An optional sortComparer keeps ids sorted.

Shape state for fast lookups

Normalised data, memoised selectors and visible middleware keep big stores manageable.

Figure 5.1 — Normalised entities feeding memoised selectors.

A todos adapter

Adapter, slice and selectors.

import { createEntityAdapter, createSlice, type PayloadAction } from '@reduxjs/toolkit';

type Todo = { id: string; text: string; done: boolean };

const todosAdapter = createEntityAdapter<Todo>({
  sortComparer: (a, b) => a.text.localeCompare(b.text),
});

const todosSlice = createSlice({
  name: 'todos',
  initialState: todosAdapter.getInitialState({ filter: 'all' as 'all' | 'open' }),
  reducers: {
    todoAdded: todosAdapter.addOne,
    todosLoaded: todosAdapter.setAll,
    todoToggled(state, action: PayloadAction<string>) {
      const todo = state.entities[action.payload];
      if (todo) todo.done = !todo.done;
    },
  },
});

export const { selectAll: selectAllTodos, selectById: selectTodoById } =
  todosAdapter.getSelectors((state: RootState) => state.todos);

A library catalogue

Books sit once on numbered shelves (entities); reading lists only hold catalogue numbers (ids). Fix a typo in a title and every list that refers to it is correct.

Quick check: What shape does createEntityAdapter give a collection?

  • A nested tree of objects
  • An ids array plus an entities lookup object
  • A Map stored in state
  • A plain array only
Answer

An ids array plus an entities lookup object — Normalised state makes lookups and updates O(1) by ID.