๐Ÿš€ Introduction

In this post, we will build a React calculator using the useReducer hook. This calculator supports basic operations like addition, subtraction, multiplication, and division, as well as percentage calculations and negation.


๐ŸŽฏ Features

  • ๐Ÿ”ข Supports basic arithmetic operations
  • ๐ŸŽญ Includes percentage and negation functions
  • ๐ŸŽจ Styled with CSS Grid & Flexbox
  • โšก Optimized state management with useReducer

๐Ÿ› ๏ธ Setup

Make sure you have Node.js installed, then run:

1
2
3
npx create-react-app calculator-app
cd calculator-app
npm install

๐Ÿ“ Code Implementation

๐Ÿ“Œ App.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
import React, { useReducer } from "react";
import DigitButton from "./DigitButton";
import OperationButton from "./OperationButton";
import "./style.css";

export const ACTIONS = {
ADD_DIGIT: "add-digit",
CHOOSE_OPERATION: "choose-operation",
CLEAR: "clear",
EVALUATE: "evaluate",
PERCENTAGE: "percentage",
NEGATE: "negate",
};

function reducer(state, { type, payload }) {
switch (type) {
case ACTIONS.ADD_DIGIT:
if (state.overwrite) {
return { ...state, currentOperand: payload.digit, overwrite: false };
}
if (payload.digit === "0" && state.currentOperand === "0") return state;
if (payload.digit === "." && state.currentOperand.includes("."))
return state;
return {
...state,
currentOperand: `${state.currentOperand || ""}${payload.digit}`,
};
case ACTIONS.CHOOSE_OPERATION:
if (state.currentOperand == null && state.previousOperand == null)
return state;
if (state.currentOperand == null) {
return { ...state, operation: payload.operation };
}
if (state.previousOperand == null) {
return {
...state,
operation: payload.operation,
previousOperand: state.currentOperand,
currentOperand: null,
};
}
return {
...state,
previousOperand: evaluate(state),
operation: payload.operation,
currentOperand: null,
};
case ACTIONS.CLEAR:
return initialState;
case ACTIONS.EVALUATE:
if (!state.operation || !state.currentOperand || !state.previousOperand) {
return state;
}
return {
...state,
overwrite: true,
previousOperand: null,
operation: null,
currentOperand: evaluate(state),
};
case ACTIONS.PERCENTAGE:
if (state.currentOperand == null) return state;
return {
...state,
currentOperand: (parseFloat(state.currentOperand) / 100).toString(),
};
case ACTIONS.NEGATE:
if (state.currentOperand == null) return state;
return {
...state,
currentOperand: (parseFloat(state.currentOperand) * -1).toString(),
};
default:
return state;
}
}

function evaluate({ currentOperand, previousOperand, operation }) {
const prev = parseFloat(previousOperand);
const current = parseFloat(currentOperand);
if (isNaN(prev) || isNaN(current)) return "";
let computation = "";
switch (operation) {
case "+":
computation = prev + current;
break;
case "-":
computation = prev - current;
break;
case "*":
computation = prev * current;
break;
case "/":
computation = prev / current;
break;
default:
return "";
}
return computation.toString();
}

const initialState = {
currentOperand: "0",
previousOperand: null,
operation: null,
};

const App = () => {
const [{ currentOperand, previousOperand, operation }, dispatch] = useReducer(
reducer,
initialState
);

return (
<div className="calculator-grid">
<div className="output">
<div className="previous-operand">
{previousOperand} {operation}
</div>
<div className="current-operand">{currentOperand}</div>
</div>
</div>
);
};

export default App;

๐Ÿ“Œ style.css

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
* {
font-family: Arial, Helvetica, sans-serif;
box-sizing: border-box;
}

body {
margin: 0;
background-color: orange;
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
}

.calculator-grid {
display: grid;
grid-template-columns: repeat(4, minmax(50px, 1fr));
grid-template-rows: minmax(90px, auto) repeat(5, minmax(60px, auto));
background-color: black;
gap: 0.8rem;
padding: 1.5rem;
border-radius: 20px;
max-width: 400px;
}

button {
cursor: pointer;
font-size: 2rem;
border-radius: 50px;
border: none;
background-color: rgb(43, 42, 42);
color: white;
padding: 15px;
transition: background-color 0.2s;
}

button:hover {
background-color: rgb(90, 90, 90);
}

๐Ÿ“Œ Summary

This project demonstrates a simple calculator built using React with useReducer for state management. The styling is implemented using CSS Grid to achieve a responsive layout. Future improvements can include additional features such as scientific calculator functions, keyboard support, or even integrating context API for better state management.

๐Ÿš€ Happy Coding!