<div id="app"></div>
/*
* https://frontendeval.com/questions/undoable-counter
*
* Create a simple counter with undo/redo functionality
*/
import React, { useState } from "https://esm.sh/react@17";
import ReactDOM from "https://esm.sh/react-dom@17";

function UndoCounter() {
  const [count, setCount] = useState(0);
  const [history, setHistory] = useState([]);
  const [undo, setUndo] = useState([]);
  
  function redoLast() {
    
    if (undo.length == 0) {
      return;
    }
    
    var lastUndo = undo.pop();
    var newUndo = undo.slice();
    var newHistory = history.slice();
    newHistory.push(lastUndo);
    var newCount = count + lastUndo.val;
    
    setCount(newCount);
    setUndo(newUndo);
    setHistory(newHistory);
  }
  
  function undoLast() {
    
    if (history.length == 0 || undo.length >= 50) {
      return;
    }
    
    var lastHistory = history.pop();
    var newUndo = undo.slice();
    var newHistory = history.slice();
    newUndo.push(lastHistory);
    var newCount = count - lastHistory.val;
    setCount(newCount);
    setUndo(newUndo);
    setHistory(newHistory);
    
  }
  
  function calculateCount(val) {
    var newHistory = history.slice();
    var newVal = count + val;
    newHistory.push({'val': val, 'before': count, 'after': newVal, 'id' : newHistory.length});
    setCount(newVal);
    setHistory(newHistory);
  }
  
  return (
    <>
      <div>
        <button onClick={undoLast}>Undo</button>
        <button disabled={undo.length == 0} onClick={redoLast}>Redo</button>
      </div>
      <button onClick={() => calculateCount(-100)}>-100</button>
      <button onClick={() => calculateCount(-10)}>-10</button>
      <button onClick={() => calculateCount(-1)}>-1</button>
      <span>  {count}  </span>
      <button onClick={() => calculateCount(1)}>+1</button>
      <button onClick={() => calculateCount(10)}>+10</button>
      <button onClick={() => calculateCount(100)}>+100</button>
      <ul>
        {history.map((row) => {
          return (
            <li key={row.id}>{row.val}  ({row.before} -> {row.after})</li>
          );
        })}
      </ul>
    </>
  );
}

const App = () => {
  return (
    <UndoCounter />
  );
}

ReactDOM.render(<App />, document.getElementById('app'));
View Compiled
Run Pen

External CSS

This Pen doesn't use any external CSS resources.

External JavaScript

  1. https://cdnjs.cloudflare.com/ajax/libs/react/19.1.1/cjs/react.production.min.js
  2. https://cdnjs.cloudflare.com/ajax/libs/react-dom/19.1.1/cjs/react-dom.production.min.js