How to Add Undo and Redo to a React Redux App
Get the project source code below, and follow along with the lesson material.
Download Project Source CodeTo set up the project on your local machine, please follow the directions provided in the README.md
file. If you run into any issues with running the project source code, then feel free to reach out to the author in the course's Discord channel.
This lesson preview is part of the Fullstack React with TypeScript Masterclass course and can be unlocked immediately with a single-time purchase. Already have access to this course? Log in here.
Get unlimited access to Fullstack React with TypeScript Masterclass with a single-time purchase.

[00:00 - 00:03] Implement, undo, and redo. Now let's implement the undo/redo functionality.
[00:04 - 00:09] To do this we'll add a history index field to our state. This field will keep track of the current undo level.
[00:10 - 00:16] We'll use its value in the app component to only render the strokes that were not undone. First let's update the root state type.
[00:17 - 00:24] Open SRC, Utils, Types, and update the root state definition. Add a new field, History, Index, Number.
[00:25 - 00:30] Now let's create actions. Open SRC actions.ts and update the action type.
[00:31 - 00:38] We want a new action with a type of undo and redo. Let's define the constants for them.
[00:39 - 00:46] Undo is undo and redo is redo. Define the action creators.
[00:47 - 00:59] Expert, const undo is a function that will return an object with type undo. Redo is the same, but instead of undo it's going to return type redo.
[01:00 - 01:06] Now update the reducer. Open SRC, root reducer.ts, update the initial state.
[01:07 - 01:13] Now History, Index is going to be 0 by default. Add the undo and redo action handlers.
[01:14 - 01:18] First case, undo. We get the history index.
[01:19 - 01:31] Its math mean between state, history, index, plus 1, and state, strokes, length. So that history index cannot be bigger than the amount of strokes that we made.
[01:32 - 01:40] So that we don't undo something that wasn't done at all in the first place. Here we return state and history index.
[01:41 - 01:43] Now let's define the redo. Okay, it's redo.
[01:44 - 01:52] Don't forget to import this action type. Here we get the history index using math, max, between state, history.
[01:53 - 01:59] Index, minus 1, and 0. And we return state, history, index.
[02:00 - 02:08] Here we are making sure that we don't go below 0 when we calculate the history index. Now we also need to update the end stroke because it will need to reset the history index.
[02:09 - 02:26] So after the state, add the history index 0, and then instead of using the regular strokes, we will slice them from index 0 till the history index. So history index will limit the amount of previous strokes that we are using.
[02:27 - 02:36] Basically undoing the strokes which index is bigger than the current history index. And we'll need to get the history index from the state, const history.
[02:37 - 02:47] Index equals state strokes, length, minus state history index. So here we basically avoid time travel paradoxes.
[02:48 - 03:01] When we undo the strokes we travel to the past, if well being in the past you draw a new stroke, the past gets altered, which makes it impossible to return to our original version of the present. Instead of creating a multiverse of paintings, we just cut off the branch of history that was undone.
[03:02 - 03:11] To make it easier to access the data from our state, let's define the selectors . Expert, const, history, index, selector.
[03:12 - 03:21] It's a function that will receive state of type root state, and will return state history index. Let's also define the stroke selector.
[03:22 - 03:34] Expert, const, strokes, selector equals a function that receives a state, root, state, and returns state strokes. Now let's create the edit panel component.
[03:35 - 03:48] Open C index CSS, and define in U CSS class, edit. It's going to have position fixed, have 40 pixels from the bottom, 30% from the left, and have a z index 10.
[03:49 - 04:09] Now inside of the SRC shared folder, create a new file, edit panel.tsx, and here we'll need to import React from React, use dispatch from React Redux, and undo and redo from the actions. Define the edit panel component, expert, const, edit panel, it's a functional component.
[04:10 - 04:16] Now inside of it, get the dispatch method. Dispatch equals use dispatch, and return the component layout.
[04:17 - 04:29] Return will have a div with class name window edit. Inside of it, we'll have the title bar div class name title bar, where we'll render another div with class name title bar.
[04:30 - 04:37] Where we'll render another div with class name title bar text. It will say edit.
[04:38 - 04:48] Below the title bar, we'll have div with class name window body. And here we'll render another div with class field row.
[04:49 - 04:59] This is where we'll render a button of class button and redo that we'll say red o and have on. Click dispatch redo.
[05:00 - 05:07] We'll have a similar button for the undo. Undo dispatch undo and have text undo.
[05:08 - 05:20] Now go to up.tsx, import the edit panel from the shared edit panel, and then render it just above the color panel. Edit panel.
[05:21 - 05:50] Now we have the UI and we have everything prepared to use the state, but we need to use this new reducer in our app component. Scroll up, and here where we use another selector, the current stroke selector, define history index equals use selector, and also the strokes const strokes equals use selector strokes selector.
[05:51 - 06:00] Now let's add the use effect to observe the history index value. Use effect, pass in a callback, we'll observe the history index.
[06:01 - 06:15] Here we'll get the canvas and the context using the get canvas with context method. We'll check if there is no context or no canvas, then we return.
[06:16 - 06:41] Otherwise we request animation frame and here we clear the canvas and then for each stroke, strokes slice zero till strokes length minus history. Index, for each of them we call draw stroke with context stroke points and stroke color.
[06:42 - 06:49] And let's format the document. Alright, so now every time history index gets updated we clear the screen and then draw only the strokes that weren't undone.
[06:50 - 06:56] Launch the app, yarn. Start, you should be able to draw and then undo and redo the strokes.