liveBook · Manning

Redux Toolkit

As explained in React in Depth

Redux Toolkit is the officially recommended package for simplifying Redux state management through standardized store configuration, reducers, and middleware.

Redux Toolkit (RTK) is a leaner way to use Redux for React data management. Classic Redux could require substantial setup even for a small list; RTK reduces that boilerplate while retaining a store-and-reducer approach. It is particularly worth considering when application data needs many coordinated updates, but it is not a default requirement for every app. section 8.0 section 8.9

The mental model is close to React’s useReducer: reducers describe updates, and actions trigger them. RTK builds Immer into its reducers, allowing update logic written against a draft. In the goal-tracking implementation, reducers add and remove goals and change which goal is selected. section 8.5 section 8.5.2

Listing 8.17 src/data/store.js
import { v4 as uuid } from "uuid";
import { createSlice, configureStore } from "@reduxjs/toolkit";
export const STORAGE_KEY = "100-things-redux";
const getInitialThings = () =>
  JSON.parse(localStorage.getItem(STORAGE_KEY)) || {
    things: [],
    currentThing: null,
  };
const reducers = {
  seeThing: (draft, { payload: newThing }) => {    #1
    draft.currentThing = newThing;   #1
  },   #1
  seeAllThings: (draft) => {    #1
    draft.currentThing = null;    #1
  },   #1
  addThing: (draft, { payload: name }) => {    #1
    draft.things.push({ id: uuid(), name, done: [] });   #1
  },   #1
  removeThing: (draft, { payload: id }) => {     #1
    const index = draft.things.findIndex((thing) => thing.id === id);   #1
    if (index !== -1) {   #1
      draft.things.splice(index, 1);   #1
      if (id === draft.currentThing) {    #1
        draft.currentThing = null;    #1
      }   #1
    }   #1
  },   #1
  doThing: (draft, { payload: id }) => {     #1
    const thing = draft.things.find((thing) => thing.id === id);   #1
    thing.done.push(Date.now());   #1
  },   #1
  undoThing:   #1
   (draft, { payload: { id, index } }) => {    #1
    const thing = draft.things.find((thing) => thing.id === id);
    thing.done.splice(index, 1);
  },
};
const dataSlice = createSlice({     #2
  name: "data",
  initialState: getInitialThings(),
  reducers,
});

export const store = configureStore({    #3
  reducer: {
    data: dataSlice.reducer,
  },
});
export const actions = dataSlice.actions;     #4

Inspect the store setup and draft-based reducers for concrete RTK update logic. section 8.5.2

Keep the data layer distinct from the components that consume it:

  • Provide the store to the React tree with react-redux’s Provider. section 8.5.2
  • Read selected values with useSelector, optionally behind application-specific hooks. section 8.5.2
  • Use useDispatch to send generated actions from hooks that perform updates. section 8.5.2
Listing 8.21 src/data/useThing.js
import { useData } from "./useData";
import { actions } from "./store";
import { useDispatch } from "react-redux";
export function useThing(id) {
  const thing = useData((store) =>
    store.data.things.find((t) => t.id === id)
  );
  const dispatch = useDispatch();     #1
  return {
    thing,
    seeThing: () => dispatch(actions.seeThing(id)),
    removeThing: () => dispatch(actions.removeThing(id)),
    doThing: () => dispatch(actions.doThing(id)),
    seeAllThings: () => dispatch(actions.seeAllThings()),
    undoThing: (index) => dispatch(actions.undoThing({ id, index })),
    undoLastThing: () =>
      dispatch(actions.undoThing({ id, index: thing.done.length - 1 })),
  };
}

See how one hook combines a selected goal with dispatch functions for viewing, removing, completing, and undoing it. section 8.5.2

Persistence is a separate implementation decision. The goal tracker reads its initial data from localStorage when defining the slice, then uses store.subscribe to write the data after store updates. RTK’s reducers do not, by themselves, provide this persistence. section 8.5.2

Choose by fit rather than habit. Context with useReducer can go far without another state library; RTK trims Redux’s setup but retains some legacy boilerplate. Zustand is simpler but offers fewer built-in patterns for organizing multiple unrelated stores. A state-machine approach such as XState may better clarify a complex flow. section 8.0 section 8.9

How React in Depth covers it

How deep it goes
Works through store, provider, selector, dispatch, and persistence files; does not detail Redux internals.
Examples
Uses a goal-tracking application with goal selection, addition, removal, completion, undo, and localStorage persistence.
What it assumes
Assumes familiarity with useReducer and builds on its reducer model to introduce Redux Toolkit.
What you can do after
Apply it via Redux Toolkit and react-redux to wire a store, component-facing hooks, actions, and local persistence.
How it connects
Connects to useReducer, Immer, react-redux, context, Zustand, and XState as related mechanisms or alternatives.

Also explained in

Compare how 2 books explain Redux Toolkit →

Related concepts

This explainer was generated by Manning's knowledge graph from the text of React in Depth. The section links point to the parts of the book it draws on.