Creating Undo-Redo System Using Command Pattern in React
Tags: react, webdev, designpatterns, tutorial
Published: 2023-07-18 04:45:27

Supposed you want to learn React by following React's official documentation. And then you come across the [Tic Tac Toe tutorial](https://react.dev/learn/tutorial-tic-tac-toe), you find it interesting and then you want to add some functionality to put it into your portfolio. (btw, If you haven't done it, please do it first, it is a good exercise for learning state management)

One of your ideas may be to add an undo-redo system. The first intuition is just to save all of the states as snapshots and add a pointer so that you can easily go back and forth between snapshots.

{% embed https://codesandbox.io/embed/nice-goodall-sdph7g?fontsize=14&hidenavigation=1&theme=dark %}

As you can see above, we have a `history` variable that contains the history of the application's states and we also have `currentMove` to track which application state we are currently in.

This is an okayish approach in this case because of the small scope. Unfortunately, this approach doesn't scale well. Imagine you are building a Photoshop, it is not feasible to save all of the states' snapshots as it might cause memory overhead. This is one of the cases that the Command Pattern comes in handy.

### Command Pattern
The command pattern is a design pattern in object-oriented programming that enables an application to perform an action by **encapsulating the necessary information** to do the action in the form of an object. Encapsulating the necessary information enables an application to trigger an action whenever they want. 

The Command Pattern consists of three main components: the invoker, the receiver, and the command. The invoker is responsible for invoking the command and is often triggered by a user action. The receiver is the controller of the action that will ultimately perform the action specified by the command. The command itself encapsulates the action to be taken, along with any necessary information.

To learn more about this pattern you can read [here](https://en.wikipedia.org/wiki/Command_pattern) and [here](https://refactoring.guru/design-patterns/command).

### Exploring Command Pattern by Creating Simple Undo-Redo Functionality
In this section, we will focus on building a simple text formatter as you can see below.

![preview of the application that we want to build](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/4tgdb424h1hbucmbr3pb.gif)

The styling of the text in the middle can be changed by clicking the action buttons below. The 3 main style modifiers are `bold`, `italic`, and `underline`. The other two buttons are for undo-redo the style changes.

To implement this using the command pattern, we need to create a base class for the command.

```
export abstract class Command<T> {
  utils: T;
  constructor(utils: T) {
    this.utils = utils;
  }
  abstract execute(): void;
  abstract undo(): void;
  abstract getInfo(): string;
}
```

This base command list all of the method that **must** be implemented by each command. If we wanted the command to be applied, then we need to trigger `execute`. We can also revert the action by calling `undo`. And to make it more intuitive, we can also show the command information by calling `getInfo`. As for `utils`, it is used to interact with the `outer` world.

Let's try implementing `bold` command.
```
export interface CommandUtils {
  styles: CSSProperties;
  setStyles: Dispatch<React.SetStateAction<CSSProperties>>;
}

export class BoldCommand<T extends CommandUtils> extends Command<T> {
  prevFontWeight?: CSSProperties["fontWeight"];
  constructor(utils: T) {
    super(utils);
    this.prevFontWeight = utils.styles.fontWeight;
  }

  getNextStyle() {
    if (
      this.prevFontWeight === "bold" ||
      (typeof this.prevFontWeight === "number" && this.prevFontWeight >= 700)
    ) {
      return "normal";
    }
    return "bold";
  }

  execute() {
    const nextFontStyle = this.getNextStyle();
    this.utils.setStyles((prevStyles) => ({
      ...prevStyles,
      fontWeight: nextFontStyle,
    }));
  }

  undo() {
    this.utils.setStyles((prevStyles) => ({
      ...prevStyles,
      fontWeight: this.prevFontWeight,
    }));
  }

  getInfo() {
    return "Bold Command : Change into " + this.getNextStyle();
  }
}
```

As you can see, it is pretty straight forward. When we instantiate the `BoldCommand`, we store the previous state. And if we decided to execute the command, we call `getNextStyle` to get the next style and apply it to the "outer" world by calling `setStyles` from `utils`. To revert the changes, we just need to call `undo` to apply previous stored state into the current styling.

The `ItalicCommand` and `UnderlineCommand` are quite similar with the code above. You can see it [here](https://github.com/mustafamilyas/command-pattern-undo-redo/blob/main/src/command.ts) or you can do it yourselves as an exercise.

The next that we need is the "receiver", the one that manage the action. In React, we can implement this in many way, but in this article, we will create a custom hook as you can see below

```
function useHistoryManager<T>() {
  const [forwardHistory, setForwardHistory] = useState<Command<T>[]>([]);
  const [backHistory, setBackHistory] = useState<Command<T>[]>([]);

  const executeCommand = async (command: Command<T>) => {
    // clear forward history
    setForwardHistory([]);
    await command.execute();
    setBackHistory((prev) => [...prev, command]);
  };
  const redo = async () => {
    if (!forwardHistory.length) return;
    const topRedoCommand = forwardHistory[forwardHistory.length - 1];
    await topRedoCommand.execute();
    setForwardHistory((prev) => prev.slice(0, -1));
    setBackHistory((prev) => [...prev, topRedoCommand]);
  };
  const undo = async () => {
    if (!backHistory.length) return;
    const topUndoCommand = backHistory[backHistory.length - 1];
    await topUndoCommand.undo();
    setBackHistory((prev) => prev.slice(0, -1));
    setForwardHistory((prev) => [...prev, topUndoCommand]);
  };

  const histories = useMemo(() => {
    const formattedBackHistory = backHistory.map((command, index) => ({
      type: "undo",
      command,
      message: command.getInfo(),
    }));
    const formattedForwardHistory = [...forwardHistory]
      .reverse()
      .map((command, index) => ({
        type: "redo",
        command,
        message: command.getInfo(),
      }));
    return [...formattedBackHistory, ...formattedForwardHistory];
  }, [backHistory.length, forwardHistory.length]);

  return {
    executeCommand,
    redo,
    undo,
    histories,
  };
}
```

`useHistoryManager` manages actions by storing action commands into two places, `forwardHistory` and `backHistory`. `backHistory` stores all of the actions that have already been done. `forwardHistory` stores all of the actions that could be done. If we want to introduce new action, we need to call it using `executeCommand`.

The last thing that we need to set up is the "invoker". To do this, we can just call our custom context and execute the command based on the API that we already made.

```
export default function App() {
  const [styles, setStyles] = useState<CSSProperties>({});
  const utils = { styles, setStyles };
  const { executeCommand, redo, undo, histories } =
    useHistoryManager<CommandUtils>();

  const setTextToItalic = async () => {
    const italicCommand = new ItalicCommand(utils);
    await executeCommand(italicCommand);
  };

  const setTextToBold = async () => {
    const boldCommand = new BoldCommand(utils);
    await executeCommand(boldCommand);
  };

  const setTextToUnderline = async () => {
    const underlineCommand = new UnderlineCommand(utils);
    await executeCommand(underlineCommand);
  };

  return (
    <div className={appStyles.container}>
      <div className={appStyles.editor}>
        <p className={appStyles.mainText} style={styles}>
          Hello from react!
        </p>
        <div className={appStyles.actions}>
          <button
            className={appStyles.button}
            onClick={setTextToItalic}
            title="italic"
          >
            <img src="/assets/format_italic.svg" alt="italic" />
          </button>
          <button
            className={appStyles.button}
            onClick={setTextToBold}
            title="bold"
          >
            <img src="/assets/format_bold.svg" alt="bold" />
          </button>
          <button
            className={appStyles.button}
            onClick={setTextToUnderline}
            title="underline"
          >
            <img src="/assets/format_underline.svg" alt="underline" />
          </button>
          <button className={appStyles.button} onClick={undo} title="undo">
            <img src="/assets/undo.svg" alt="undo" />
          </button>
          <button className={appStyles.button} onClick={redo} title="redo">
            <img src="/assets/redo.svg" alt="redo" />
          </button>
        </div>
      </div>
      <div className={appStyles.history}>
        <h2 className={appStyles.historyHeader}>History</h2>
        <ol className={appStyles.historyContent}>
          <li className={`${appStyles.historyItem}`}>
            <button>Initial</button>
          </li>
          {histories.map((history, index) => (
            <li
              className={`${appStyles.historyItem} ${
                history.type === "undo" ? appStyles.undo : appStyles.redo
              }`}
              key={index + history.type + history.message}
            >
              <button>{history.message}</button>
            </li>
          ))}
        </ol>
      </div>
    </div>
  );
}
```

Hooray!, we can create undo-redo using the command pattern. If you want to see this live in action, click [here](https://command-pattern-undo-redo.vercel.app/).

![preview of the application that we want to build](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/4tgdb424h1hbucmbr3pb.gif)

### Conclusion
With the command pattern, we can simplify action information into a series of objects that can be triggered at a convenient time. But the usage of command pattern is very limited, as this pattern requires a lot of boilerplates that in many cases we don't necessarily need them.

All of the codes above can be found on [this repository](https://github.com/mustafamilyas/command-pattern-undo-redo). If you have any comments or feedback, feel free to leave a comment below. Will be happy to discuss this with you all. Cheers!

### Further Reading
1. [React Tic Tac Toe](https://react.dev/learn/tutorial-tic-tac-toe)
2. [Command Pattern - Wikipedia](https://en.wikipedia.org/wiki/Command_pattern)
3. [Command Pattern - Refactoring.guru](https://refactoring.guru/design-patterns/command)