Add undo & redo functionality in your APP
Tags: javascript, programming, tutorial, codenewbie
Published: 2021-11-14 12:56:21

halo! Opps I spelled it wrong. Let me Ctrl+z this. Ohh wait your app doesn't support undo and redo, what a shame :( Okay in this article lets fix that.
But instead of working with a big and complex project lets use this simple class that does some calculations.
```js
class Calculate {
    constructor(initialValue){
        this.value = initialValue
    }
    add(val){
        this.value += val
    }
    sub(val){
        this.value -= val
    }
    mul(val){
        this.value *= val
    }
    div(val){
        this.value /= val
    }
}


const num = new Calculate(0)
num.add(10) // Value: 0 + 10 = 10
num.add(20) // Value: 10 + 20 = 30
num.mul(2) // Value: 30 * 2 = 60
num.sub(100) // Value: 60 - 100 = -40
console.log(num.value) // Output: -40
```

Now we want to add undo and redo functionality to our class.
So basically we should be able to so this
```js
const num = new Calculate(0)
num.add(10)
num.add(20)
num.mul(2)
num.sub(100)

num.undo()
num.undo()
num.redo()

console.log(num.value) // Expected output: 60
```

Okay so first we will create another class called `Executor` that will have a `execute` function that take two functions: 
1. Function that does the indented operation
2. Function that undo the operation
```js
class Executor {
    constructor(){
        // Stores the undo functions
        this.undoQueue = []
        // Stores the redo functions
        this.redoQueue = []
    }
    execute(fn, undoFn){
        fn()
        this.undoQueue.push(() => {
            undoFn()
            // The redo will be added to queue only after the undo is executed
            this.redoQueue.push(fn) // calling fn after undoFn will be redoing the same operation
        })
    }
    undo(){
        if(this.undoQueue.length > 0){
            this.undoQueue.pop()()
        }
    }
    redo(){
        if(this.redoQueue.length > 0){
            this.redoQueue.pop()()
        }
    }
}
```

So now lets use the `Executor` in our `Calculate` class.
```js
class Calculate {
    constructor(initialValue){
        this.value = initialValue
        this.executor = new Executor()
    }
    add(val){
        this.executor.execute(
            () => this.value += val, //Function to execute
            () => this.value -= val //Function to undo
        )
    }
    sub(val){
        this.executor.execute(
            () => this.value -= val, //Function to execute
            () => this.value += val //Function to undo
        )
    }
    mul(val){
        this.executor.execute(
            () => this.value *= val, //Function to execute
            () => this.value /= val //Function to undo
        )
    }
    div(val){
        this.executor.execute(
            () => this.value /= val, //Function to execute
            () => this.value *= val //Function to undo
        )
    }
    redo(){
        this.executor.redo()
    }
    undo(){
        this.executor.undo()
    }
}
```
So now we can simply use `num.undo()` to undo the operation and `num.redo()` to redo it.
So here is the full code
```js
class Calculate {
    constructor(initialValue){
        this.value = initialValue
        this.executor = new Executor()
    }
    add(val){
        this.executor.execute(
            () => this.value += val,
            () => this.value -= val
        )
    }
    sub(val){
        this.executor.execute(
            () => this.value -= val,
            () => this.value += val
        )
    }
    mul(val){
        this.executor.execute(
            () => this.value *= val,
            () => this.value /= val
        )
    }
    div(val){
        this.executor.execute(
            () => this.value /= val,
            () => this.value *= val
        )
    }
    redo(){
        this.executor.redo()
    }
    undo(){
        this.executor.undo()
    }
}

class Executor {
    constructor(){
        this.undoQueue = []
        this.redoQueue = []
    }
    execute(fn, undoFn){
        fn()
        this.undoQueue.push(() => {
            undoFn()
            this.redoQueue.push(fn)
        })
    }
    undo(){
        if(this.undoQueue.length > 0){
            this.undoQueue.pop()()
        }
    }
    redo(){
        if(this.redoQueue.length > 0){
            this.redoQueue.pop()()
        }
    }
}

const num = new Calculate(0)
num.add(10)
num.add(20)
num.mul(2)
num.sub(100)

num.undo()
num.undo()
num.redo()

console.log(num.value) // Output: 60
```


## Make sure you checkout my other articles and YouTube channel
{% user 0shuvo0 %}
## Was it helpful? Support me on Patreon
[![Patreon Logo](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/7vnw8g7vs5pok3jwgtgv.png)](https://www.patreon.com/0shuvo0)