Adding undo to a React/Redux app

Scorekeeper
The app we’re going to modify.

Building the UI

Before we talk through how to handle undo history data let’s take care of the easy stuff: adding the undo and redo buttons to the app. Run the following commands to download Scorekeeper, install the dependencies, and start the app.

git clone git@github.com:Lab43/scorekeeper.git
cd scorekeeper
npm install
npm start
src/components/UndoButton.js
src/components/RedoButton.js
src/components/Scoreboard.js
src/styles.css
Scorekeeper with non-functional undo and redo buttons

The history stack

Redux’s documentation suggests implementing undo by saving the previous states of your data. Then you can just replace the current state with the previous state whenever the user clicks undo. We’re going to take a different approach: storing Redux actions that can be dispatched to rewind or replay state changes. I’m going to call this the history stack. The big advantage to this approach is that we can also store thunk functions in the history stack that trigger side effects when dispatched, like API calls.

  1. Delete the player Emma.
  2. Add a new player.
  3. Name the new player Agnes.
  4. Give Agnes 1 point.
    Do/Redo Action                 Undo Action
-----------------------------------------------------------
Remove Emma Restore Emma
Create new player (Player 4) Remove Player 4
Rename Player 4 to Agnes Rename Agnes to Player 4
👉 Give 1 point to Agnes Take away 1 point from Agnes
    Do/Redo Action                 Undo Action
-----------------------------------------------------------
Remove Emma Restore Emma
Create new player (Player 4) Remove Player 4
👉 Rename Player 4 to Agnes Rename Agnes to Player 4
Give 1 point to Agnes Take away 1 point from Agnes
    Do/Redo Action                 Undo Action
-----------------------------------------------------------
Remove Emma Restore Emma
Create new player (Player 4) Remove Player 4
Rename Player 4 to Agnes Rename Agnes to Player 4
👉 Give 1 point to Ratna Take away 1 point from Ratna

Building the Redux history store

Naturally our history stack will live in Redux. Create a file named history.js in src/store for the Redux actions, reducer, and action creators. We’re going to build this file up bit-by-bit, starting with the ability to add entries to the history stack:

src/store/history.js
dispatch(doThing());
dispatch(addHistory(
doThing();
undoThing();
);
dispatch(withHistory(
doThing();
undoThing();
));
src/store/index.js

Adding history from existing action creators

To add entries to the history stack all we have to do is dispatch addHistory or withHistory whenever we make state changes. The best place to do this is from the other action creators in our app. The only other file with action creators is the players store in src/store/players.js , so that’s the only file that needs to be updated. We don’t have to touch any components!

Excerpt from src/store/players.js
Excerpt from src/store/player.js

Wiring up the undo and redo buttons

Every action a user takes in the app is now adding entries to the history stack, but we can’t actually undo or redo anything because we haven’t hooked those buttons up yet. First we need to add some features to the Redux history store. Update your src/store/history.js file like this:

src/store/history.js
src/components/UndoButton.js
src/components/RedoButton.js
Scorekeeper with undo and redo working.

--

--

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store
Cameron Spencer

Cameron Spencer

I’m a full-stack web developer with a background in design and over 10 years of experience. Learn more at https://www.lab43.com