<div id="app"></div>
.App{
  display:flex;
  flex-direction:column;
  justify-content:center;
  align-items:center;
}
.FlexContainer{
   display:flex;
  flex-direction:column;
  justify-content:center;
  align-items:center;
  
}

.GenericButton{
  margin-right:5px;
}

.MinusOne{
  margin-right:25px;
}
.PlusOneHundred{
  margin-left:30px;
}

.Undo{
  margin-right:30px;
}

.FlexContainer__FirstRow{
 margin-bottom:20px;
}

.GenericButton{
/*   width:30px;
  height:30px; */
}

.TopRow{
/*   margin:10px; */
  justify-content:space-between;
}

.HistoryTable{
  border:1px solid black;
  width:40vw;
  display:flex;
  flex-direction:column;
  
}

.HistoryTable__row{
padding-left:10px;
padding-bottom:10px;
margin-top:10px;
border-bottom:1px solid black;
text-align:center;
}

.HistoryTable__Counters{
  margin-left:20px;
}
.HistoryTable__row:last-child{
  border:none;
}


/*
* https://frontendeval.com/questions/undoable-counter
*
* Create a simple counter with undo/redo functionality
*/
const HistoryTable=({history})=>{
  return(history.length>1 && history.length<50 &&(
    <>
          <h2>History</h2>
  <div className="HistoryTable">
 { history.slice(0,history.length-1).reverse().map((item)=>{
        const{action,currentCounter,futureCounter}=item
        return(
      <div className="HistoryTable__row">    
     <span className="HistoryTable__Action">{action>0?`+${action}`:action}</span>
      <span className="HistoryTable__Counters">({currentCounter} -> {futureCounter})</span>
      </div>      
  )})}
    
    </div>
    </>        )
           
 )
}


const Counter=()=>{
    //Counter buttons:
    //    //pushes {action,currentCounter,futureCounter}
    //adds or subtracts to master counter
  const[masterCounter,setMasterCounter]=React.useState(0)
  const [historyTable,setHistoryTable]=React.useState([])
  const [masterHistory,setMasterHistory]=React.useState([])
  const [undoClickedCounter,setUndoClickedCounter]=React.useState(0)

  const lastItem=(arr)=>arr[arr.length-1]
  React.useEffect(()=>
  console.log("undoClickedCounter",undoClickedCounter,masterHistory),[undoClickedCounter])
  const counterHandler=(action)=>{
    setMasterCounter((prevState)=>prevState+=action)
//keeps all history of actions 
    setMasterHistory((prevState)=>prevState.concat(action))
//controls what is shown on screen - immediate history
    setHistoryTable((prevState)=>prevState.concat({action,currentCounter:masterCounter,futureCounter:masterCounter+action}))}
  
  const undoHandler=()=>{
    if(historyTable.length>0){
     //track how many times undo clicked
      setUndoClickedCounter((prevState)=>prevState+=1)
    //get last object in array
    const prevHistory=historyTable[historyTable.length-1]
   //set counter equal to opposite of prevAction
    setMasterCounter((prevState)=>prevState-=prevHistory.action)
    //pop out last item in history
     let historyTableCopy=[...historyTable]
     historyTableCopy.pop()
    setHistoryTable(historyTableCopy)
    }
 
    
    
  }
  const redoHandler=()=>{
    if(masterHistory.length>0){
      //track how many times undo clicked
      setUndoClickedCounter((prevState)=>prevState-=1)
       //take last item of masterHistory
    const redoItem=masterHistory[masterHistory.length-1]
    //set history/counter to this item
    setMasterCounter((prevState)=>prevState+=redoItem)
setHistoryTable((prevState)=>prevState.concat({action:redoItem,currentCounter:masterCounter,futureCounter:masterCounter+redoItem}))
      //set masterHistory using undoClickCounter
    }
   
  }
  
  return(
    <div className="App">
   <h1>Undoable Counter</h1>
        
  <div className="FlexContainer">
      <div className="FlexContainer__FirstRow">
        <button className="EditButton Undo" onClick={undoHandler}>Undo</button>
        <button className="EditButton Redo" disabled={undoClickedCounter<=0} onClick={redoHandler}>Redo</button>
      </div>
      <div className="FlexContainer__SecondRow">
        <button className="GenericButton -100" onClick={()=>counterHandler(-100)}>-100</button>
        <button className="GenericButton -10" onClick={()=>counterHandler(-10)}>-10</button>
        <button className="GenericButton MinusOne" onClick={()=>counterHandler(-1)}>-1</button>
          {masterCounter}
          <button className="GenericButton PlusOneHundred" onClick={()=>counterHandler(100)}>+100</button>
        <button className="GenericButton +10" onClick={()=>counterHandler(10)}>+10</button>
        <button className="GenericButton +1" onClick={()=>counterHandler(1)}>+1</button>
      </div>
  </div>
    <HistoryTable history={historyTable}/>
      </div>
  )
}

const App = () => {
  return( <Counter/>)
}

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/17.0.1/umd/react.production.min.js
  2. https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.1/umd/react-dom.production.min.js