In the last blog post, I explained how I created the layout for my practice calculator app. This time around, I’m going to go through the process that I used to make it work.
Adding Redux
To be clear: Redux is not strictly necessary for this example, as I probably could have gotten away with passing props up and down and keeping the state in a single lifted component. But I wanted the opportunity to practice with it, and this seemed like a good way to better familiarize myself with its features.
When working with Redux, the first thing I did was consider what would need to be held in state. Because I wanted a simple calculator that could perform the four basic functions and a few extras, I figured I could keep the state simple: I would need to store both numbers and the function that was being performed on them.
To that end, I created two reducers: a numReducer
which stored the first and second numbers, and a funcReducer
which stored the current function.
I then combined them using Redux’s combineReducers
function:
There are additional reducers I needed to add, but I’ll cover them as they come up.
Number Pad Functions
Next, I needed to add the most basic element of the calculator: the number buttons. When a number button is pressed, that amount is added onto the end of the number currently displayed on the calculator. I implemented this by creating a Redux action, SETNUMKEYPAD
, which told the reducer that a digit was going to be appended to the end of the number.
Pretty much all of my actions follow this same formula. The actual logic happens in the reducer.
By multiplying the existing number by 10 and adding the new digit, we create a system where the number is built from right to left, just like a calculator.
The dispatch is then called in the Buttons.js
file, where NumButton
is given a handleClick
functiont hat calls setNumKeypad
.
However, we quickly run into a problem: the calculator is storing two numbers, but how do we tell it which number to edit? I solved this by adding a new piece of state to Redux: a display
value that determined which of the two numbers the display was showing at any given time.
From there, I created a DisplayController
class which served as the “smart” component to the “dumb” Display
component. This component simply shows which of the two nums
values display
is calling for at that time.
Because display
uses the same strings (“FIRST” and “SECOND”) as the names of the two nums in the nums
piece of state, it’s easy to pass the value of display
to the setNumKeypad
function, ensuring that only the number that is currently on the screen is changed.
Thus, pressing on the buttons allows for the number displayed to be altered, but not the other number saved in the system.
Adding the Four Functions
Now that we can set numbers, it’s time to implement the feature that makes a calculator a calculator: the ability to calculate things.
This version has four primary functions: addition, subtraction, multiplication, and division. The current function being used is saved in the func
piece of state, and is stored as that function’s symbol (“+”, “-“, “×”, and “÷”).
The actual calculation process is a simple switch
statement, evaluating the two numbers and changing the first number into the solution. This is done in order to support chaining functions (e.g., adding then subtracting) or repeating the same function multiple times.
The equals button then calculates those values, and also sets a new piece of state, setClearOnNext
, to true
. This value determines if the calculator is primed to accept a new set of values for a new calculation; without it, subsequent entries on the keypad would alter the result, rather than starting a new number.
I also added a little more logic to the act of actually pressing the function buttons. If the “clearOnNext” variable is true, then it is set to false and new values aren’t calculated (since we are preparing to perform a new operation). The second number is also preemptively cleared, although the display is not set to show it.
The responsibility of actually changing the display value falls on the number buttons, since I don’t want a new value to be shown until it’s absolutely necessary. Additionally, I have the button check if the clearOnNext
value is true; if so, they clear all values from the calculator and reset them before entering the new value.
Additional Functions
With the four main functions working, it’s time to turn to the other buttons on the calculator. Most of these are fairly straightforward and did not require extensive additions.
Negating Values
For the “+/-“ button, I created a new action, REVERSESIGN
and returned the existing value multiplied by -1
:
Decimals
Adding decimals was a bit trickier, becuase the calculator needed to understand that, once the decimal button was pressed, subsequent number buttons pressed needed to be added to the number in a different way.
I solved this by introducing an additional element of state: decimalPlace
, which counts the number of decimal places in the current number. If this value is zero, then the program functions normally. It is incremented when the “.” button is pressed and then on every number button press after. The value is then used to calculate the addition of a new value after the decimal, by multiplying the new digit by 10 raised to the power of the value. The handleClick
function in Buttons.js
was thus rewritten as follows:
The decimalPlace
value is reset whenever the display changes.
Clearing Everything
Adding functionality to the AC
button was extremely simple, as I had already written an action that reset the calculator (for use after the “=” button was pressed). Most of my reducers have a case to listen for this action, and they reset accordingly.
Percent Button
Finally, I added functionality to the “%” button. This button is used to calculate the percent of the first number entered; as such it has a few caveats. If pressed when still entering the first number, it returns the value to 0. If pressed after entering the second number, however, it considers the second number to be a percentage and returns the percent value of the first number.
Note that this does not calculate anything else; it does not replace one of the four functions, for example. This makes it possible to perform actions such as subtracting or adding a certain percent of a number.
Conclusions
This took longer than I meant it to, mostly due to a lot of things in my personal life going haywire all at once. I’m hoping not to take this long for my next blog post. I have another small React project in the works, and after that I’m fairly confident that I’m ready to tackle the frontend of my Grocery App. I’ve been talking with my brother to do some markups of how it’s going to look and I’m pretty excited, so stay tuned!