<div id="app"></div>
.counter-display {
display: flex;
justify-content: space-between;
}
.counter-display > * {
padding: 1rem;
}
#count-display {
font-size: 1.2rem;
margin: 0 1rem;
}
.main {
max-width: 500px;
margin: 0 auto;
}
.main > h1 {
text-align: center;
}
.history {
text-align: center;
width: 50%;
margin: 0 auto;
margin-top: 50px;
}
.history-box {
border: 1px solid black;
height: 150px;
overflow-y: auto;
}
ul.history-box {
list-style-type: none;
padding: 0;
}
ul.history-box li {
display: flex;
justify-content: space-around;
}
.controls {
display: flex;
justify-content: center;
padding: 1rem;
}
.controls > button {
padding: 1rem;
margin: 1rem;
}
button {
cursor: pointer;
}
/*
* https://frontendeval.com/questions/undoable-counter
*
* Create a simple counter with undo/redo functionality
*/
import { useState } from "https://cdn.skypack.dev/react@17.0.1";
import ReactDOM from "https://cdn.skypack.dev/react-dom@17.0.1";
function Counter({ history, setHistory, count, setCount }) {
function handleClick(num) {
const historyObj = {
action: num,
before: count,
after: count + num
};
setHistory([...history, historyObj]);
setCount(historyObj.after);
}
return (
<div className="counter-display">
<button onClick={() => handleClick(-100)}>-100</button>
<button onClick={() => handleClick(-10)}>-10</button>
<button onClick={() => handleClick(-1)}>-1</button>
<div id="count-display">{count}</div>
<button onClick={() => handleClick(1)}>+1</button>
<button onClick={() => handleClick(10)}>+10</button>
<button onClick={() => handleClick(100)}>+100</button>
</div>
);
}
function History({ history }) {
const histories = history.map((historyObj, index) => {
return (
<li key={index}>
<span>{historyObj.action}</span>
<span>{`${historyObj.before} -> ${historyObj.after}`}</span>
</li>
);
});
return (
<div className="history">
<h2>History</h2>
<ul className="history-box">{histories}</ul>
</div>
);
}
function App() {
const [history, setHistory] = useState([]);
const [redos, setRedos] = useState([]);
const [count, setCount] = useState(0);
function handleUndoClick() {
if (history.length === 0) {
return;
}
const newHistory = history.slice(0); // pop
redos.push(newHistory.pop());
setRedos(redos);
setHistory(newHistory);
if (newHistory.length !== 0) {
setCount(newHistory[newHistory.length - 1].after);
} else {
setCount(0);
}
}
function handleRedoClick() {
if (redos.length === 0) {
return;
}
const redo = redos.pop();
const newHistory = [...history, redo];
setHistory(newHistory);
setRedos(redos);
setCount(newHistory[newHistory.length - 1].after);
}
return (
<div className="main">
<h1>Undoable counter</h1>
<div className="controls">
<button onClick={handleUndoClick}>Undo</button>
<button onClick={handleRedoClick}>Redo</button>
</div>
<Counter
history={history}
setHistory={setHistory}
count={count}
setCount={setCount}
/>
<History history={history} />
</div>
);
}
ReactDOM.render(<App />, document.getElementById('app'));
View Compiled
This Pen doesn't use any external CSS resources.