Let's create an iOS Calculator Clone in React [+ detailed explanations]
Tags: react, javascript, codenewbie, webdev
Published: 2021-07-03 15:36:25

Hello everybody! 🚀

Today we will be creating an __iOS calculator clone using React__.

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/061tj2jsax27mlq4nbpk.png)

This __tutorial__ comprises __3 parts__: the components structure, the calculator's interface and the functionality.

Let's dive into it and __start by creating the components and their structures__.

#1. The components

Since we're using a __component-based approach__, we're going to try to __modularize our app as much as we can__, so with that vision in mind, we'll split it up into __4 components__, namely, the following: 

###· Calculator.js
This will be the __main component__. The one who will hold all the __logic__ and __functionality__, and will __interact__ with the rest of the component through __props__.

###· Display.js
The __screen__ of the calculator. It will receive a __value__ that will be __displayed__ on screen.

###· Keypad.js
The __keypad__ of the calculator. It will be divided into a few sections, depending on the functionality of every specific __set of buttons__.

###· Button.js
A __reusable__ component __for every button__ in the calculator. 

___

Now that we've learned about the components we're going to work with, let's turn our attention to the structure of each of them.

##1.1. Calculator.js
The __parent component__,  which will be in charge of all the __functionality__ and the one __managing the state__ of the whole calculator app.
```jsx
import React, { useEffect, useState } from "react";
import Display from "./Display";
import Keypad from "./Keypad";

const Calculator = () => {
  ...
  return (
    <div id="calculator-view" className={"flex column jc-center ai-center"}>
      <div id="viewport" className={"flex column jc-sp-between ai-center"}>
        <Display value={screenValue} />
        <Keypad actionToPerform={handleActionToPerform} allClear={isScreenClear} />
      </div>
    </div >
  )
}
```

This main component is going to call a couple of different custom components: __Display__ and __Keypad__, so they need to be imported up above.

>Note that the ellipsis (...) in this code snippet means that we'll be injecting some __state__ and __functionality__ for the component soon later.

>For now, let's just focus on the structure of the components themselves.

##1.2. Display.js

A very simple component that just __receives a value__ and shows it on screen, as mentioned earlier above.

```jsx
import React from "react";

const Display = (props) => {
   const { value } = props;
   return (
      <div id="display" className="flex">
         <input type="text" tabIndex="-1" value={value} />
      </div>
   )
}

export default Display;
```


##1.3. Keypad.js

Keypad is a component whose function is to __serve as a gateway between the calculator and the buttons__.

```jsx
import React from "react";
import Button from "./Button";
const Keypad = (props) => {
   const { actionToPerform, allClear } = props;
   ...
   const handleClickButton = (value, keyType) => {
      actionToPerform(value, keyType);
   }
   return(
      <div id="keypad" className="flex row jc-sp-around">
         <div className="grid">
            {functionKeys.map(
                    functionKey =>
                        <Button key={functionKey.label} label={functionKey.label} value={functionKey.value}
                            buttonStyle="fx-key" onClick={handleClickButton} type="fx" />
             )}
            {numericKeys.map(
                    numericKey =>
                        <Button key={numericKey} label={numericKey} value={numericKey}
                            buttonStyle="numeric-key" onClick={handleClickButton} type="numeric" />
             )}
            {lastRowKeys.map(
                    lastRowKey =>
                        <Button key={lastRowKey.label} label={lastRowKey.label} value={lastRowKey.value}
                            buttonStyle={lastRowKey.buttonStyle} onClick={handleClickButton} type={lastRowKey.type} />
             )} 
         </div>
         <div className="flex column jc-sp-btw">
            {operatorKeys.map(
                    operatorKey =>
                        <Button key={operatorKey.label} label={operatorKey.label} value={operatorKey.value}
                            buttonStyle="op-key" onClick={handleClickButton} type="operator" />
             )}
         </div>
      </div>      
   )
}

export default Keypad;

```
This component contains a bunch of buttons (don't forget to import the _Button_ component 🙂), which when pressed, __send back some information about their functionality and type__. The keypad, in turn, will send this data to the Calculator component.

Since it's a good practice to __abstract__ your data as much as you can (always in a moderate way), we'll be using __arrays to define every set of buttons__ instead of defining a button component every time we need to create one. This is useful for many reasons.

One of them, among others: Let's say that you wish to __change the name__ of the _Button_ component to _Key_. If you're calling the component 10 times, you'd have to change the name of the component 10 times. However, if you map through an array that creates a _Button_ component in every iteration, you'd just have to __make the change once__.

Let's have a look at how these arrays are defined and structured:

```jsx
const numericKeys = [7, 8, 9, 4, 5, 6, 1, 2, 3];

const operatorKeys = [
   { label: "÷", value: "/" },
   { label: "×", value: "x" },
   { label: "-", value: "-" },
   { label: "+", value: "+" },
   { label: "=", value: "=" }
];

const functionKeys = [
   { label: allClear ? "AC" : "C", value: allClear ? "AC" : "C" },
   { label: "±", value: "+/-" },
   { label: "%", value: "%" }
];

const lastRowKeys = [
   { label: "0", value: "0", type: "numeric", buttonStyle: "numeric-key special" },
   { label: "·", value: ".", type: "fx", buttonStyle: "numeric-key" }
];
```

For __numeric keys__, we just have an __array of__ ___integers___, ordered by the order of occurrence of each of them.

For __operator__ and __function keys__, we have an __array of objects__, each of them containing a _label_ and a _value_.

For __last row keys__ (they receive this name because they appear at the bottom but have different functionalities as to organize them based on that), we have as well an __array of objects__, each of them comprising a _label_, a _value_, a _type_ and a _buttonStyle_.


##1.4. Button.js

A __reusable__ component to __define buttons__.

```jsx
import React from "react";

const Button = (props) => {
    const { value, type, buttonStyle, label, onClick } = props;

    const handleButtonClick = () => {
        onClick(value, type);
    }
    return (
        <button name={value} className={buttonStyle} onClick={handleButtonClick}>
            {label}
        </button>
    );
};

export default Button;
```

This component just renders __a regular HTML button element__.

#2. The interface

In this tutorial, we're cloning an existent app, so our __replica__ should be __the most faithful possible to the original interface__.

For styling the interface, we'll be using __SCSS__. But of course you can use whichever styling language / tool / resource of your choice: __CSS__, __SASS__, __LESS__, __PostCSS__, __Styled Components__...

Here's the code:

```scss
//color variables
$white: #fff;
$black: #000;
$dark-gray: #333;
$medium-gray: #444;
$gray: #a5a5a5;
$light-gray: #c4c4c4;
$orange: #ff9d20;
$light-orange: #ffb657;

* {
    font-family: "Source Sans Pro", sans-serif;
    font-weight: 200;
    color: $white;
}

.flex {
    display: flex;
}

.row {
    flex-flow: row nowrap;
}

.column {
    flex-flow: column wrap;
}

.flex-end {
    justify-content: flex-end;
}

.jc-sp-btw {
    justify-content: space-between;
}

.jc-sp-around {
    justify-content: space-around;
}

.jc-center {
    justify-content: center;
}

.ai-center {
    align-items: center;
}

.grid {
    display: grid;
    grid-template-columns: repeat(3, auto);
    gap: 9px; 
}

#calculator-view {
   width: 385px;
   height: 775px;
   background-color: $black;
   border-radius: 70px;
   border: 10px solid $dark-gray;
   #viewport {
      width: 90%;
      height: 70%;
      #display {
         width: 100%;
         input {
            border: none;
            outline: none;
            font-size: 6rem;
            background-color: $black;
            width: 100%;
            text-align: right;
            padding-right: 20px;
         }
      }
      #keypad {
         width: 97%;
         button {
            border: none;
            border-radius: 50px;
            width: 75px;
            height: 75px;
            font-size: 2rem;
            cursor: pointer;
            &.fx-key {
               background-color: $gray;
               color: $black;
               &:hover {
                  background-color: $light-gray;
               }
            }
            &.numeric-key {
               background-color: $dark-gray;
               &:hover {
                  background-color: $medium-gray;
               }
            }
            &.op-key {
               background-color: $orange;
               font-size: 3rem;
               &:hover {
                  background-color: $light-orange;
               }
            }
            &.special {
               width: 100%;
               grid-column-start: 1;
               grid-column-end: span 2;
               text-align: left;
               padding-left: 25px;
            }
         }
      }
   }
}


```

There's not really much to explain here. We're just simulating the interface of the iOS calculator and this snippet of code would be it! 😇

#3. The functionality

Let's start by defining __the overall state__ for the calculator (specified and managed in the calculator component).

```jsx
const Calculator = () => {
   const [accValue, setAccValue] = useState(null);
   const [screenValue, setScreenValue] = useState("0");
   const [currentOperator, setCurrentOperator] = useState(null);
   const [expectsOperand, setExpectsOperand] = useState(false);
   ...
}

export default Calculator;
```

##What's the main idea in here?
Well, we need to divide our component state into __four__ ___chunks___ (the minimum required __for our calculator to behave as a calculator__):
  - __accValue__: the accumulated value in the calculator. It starts off as _null_ because there's no accumulated value initially.

  - __screenValue__: the value that is going to be shown on screen. Initially, its value is _"0"_. Note that we're defining it as a __string__, not a __number__. We'll talk about this later. 

  - __currentOperator__: the ongoing operator. As well as _accValue_, it starts off as _null_ for the same reason.

  - __expectsOperand__: a __boolean__ that lets the calculator know __if a new operand should be entered__ after pressing a button or if, on the contrary, a result, which is __final itself__, has been already calculated.
It will become __true__ when an __operator key__ is pressed, and __false otherwise__ (__only operations wait for a second operand__. Neither numbers nor functions that apply to a single operand). It starts off as _false_, since the initial state itself is _stable_.

Let's now take a look at the __different types of functionality that our calculator will implement__ and their __associated keys/buttons__.

But first, let me show you the __handler__ that will be called every time a key (_Button_ component) is pressed. It receives the __value of the key__ and the __key type__ (function, numeric or operator) as parameters. The handler itself will call a __different function__ depending on the value of _keyType_:

```jsx
const handleActionToPerform = (value, keyType) => {
   switch (keyType) {
      case "fx": handleClickFunctionKey(value); break;
      case "numeric": handleClickNumericKey(value); break;
      case "operator": handleClickOperator(value); break;
   }
}
```
##3.1. Function keys
The function keys are those who __implement a function over a single operand__ or functions __related to the screen__.

This is what happens when we click on a function button:

```jsx
const handleClickFunctionKey = value => {
   switch (value) {
      case "AC": allClear(); break;
      case "C": clearScreen(); break;
      case "+/-": reverseSign(); break;
      case "%": percentage(); break;
      case ".": addDecimalPoint(); break;
   };
 }
```
We have implemented a _switch_ statement that __decides which function is executed next__ based on the passed in value.

The few different __function keys__ in our calculator __implement the following actions__:

###3.1.1. All clear and clear screen functions: AC/C

The __allClear__ function (AC) __clears everything__ and __resets every value__ to their initial state.

```jsx
const allClear = () => {
   setAccValue(null);
   setScreenValue("0");
   setCurrentOperator(null);
   setExpectsOperand(false);
}
```
The __clearScreen__ function (C) __clears the value of the current screen__, but the __rest of the state remains the same__.

```jsx
const clearScreen = () => {
   setScreenValue("0");
}
```

These two functions are available __through the same button__, so we need to have a __boolean variable who manages__ the current _state_ of the screen (clear or not) at all times, to be able to know __which one of them should be shown as the label__ of the button. That's the reason why this variable is passed to the _Keypad_ component as a _prop_.

```jsx
const isScreenClear = screenValue === "0";
```

###3.1.2. Reverse sign function: +/-

The __reverseSign__ function, as its name indicates, __reverses the sign of the screen value__.

```jsx
const reverseSign = () => {
   setScreenValue(String(-parseFloat(screenValue)));
}
```

####String? parseFloat?

Well, it's time to mention __how the data is displayed and stored__ in the calculator. Let's bear in mind the following fact:


- What we see on __screen__ is a __value__ stored as a ___string___ and the __values with which we operate__ are stored as ___float numbers___.

You may be wondering why we don't use floats directly on the screen. The answer is because __we could never see something like__ ___0.___ __using a float__. That's only possible by using a ___string___. 

As easy as that :) 

So, in this particular case, we are __parsing the screen value__ (currently a _string_) into a ___float___ value, then we __reverse its sign__, and then we __reconvert__ it to ___string___ to show it on screen.

###3.1.3. Percentage function: %

The __percentage__ function __divides the screen value by 100__.

```jsx
const percentage = () => {
   setScreenValue(String(parseFloat(screenValue)/100));
};
```
We're applying the same method to set the new screen value (__retrieve__ the current screen value, __parse__ it to _float_, __operate__ with it and __reconvert__ it to _string_).


###3.1.4. Decimal point function: . 

The __addDecimalPoint__ function adds a _dot_ to _visually transform_ the current screen number into a float-like number (we're actually operating with _floats_, but remember that the screen value is a _string_ and if we were using a _float_ value directly, we could never see something like _0._ or _3._).

```jsx
const addDecimalPoint = () => {
   if (expectsOperand) {
      setScreenValue("0.");
   }
   else {
      if (!screenValue.includes("."))
         setScreenValue(screenValue + ".");
   }
   setExpectsOperand(false);
}
```
Let's stop for a minute to understand the idea of this function.

When adding a dot (to let the user know that they can add decimals to the number shown on screen), we need to be a little bit __more cautious than with the rest__ of operations.

Let's propose these scenarios: 

__If the calculator is waiting for an operand__, that is, the next step is adding a second operand (let's say we want the second operand to be __0.5__), and we __directly press__ on the decimal point key (without pressing a numeric key before), __a 0 should be appended in front of that dot__. The calculator must not in any case show an operand starting by a dot (_.5_, for example).

But __if the calculator isn't waiting for anything else__, that is, the current state is stable (the screen value is a full operand and makes sense by itself, although we still have the possibility of adding more digits), __a dot will be concatenated__ to the screen value __if and only if there's no other dot in it__. __Otherwise__, the screen number will __remain the same__. A number can't have two decimal parts 😰

###3.1.5. Delete last digit function: <-

In this calculator prototype, a __button for removing the last digit is not provided__, so we're going to __emulate such behavior__ by using the __backspace key__ of the keyboard.

>Note that this is __the only one functionality__ that we're going to implement to be used __through the keyboard__, not number, operator or function keys. For those, we'll be using the __keypad buttons__, not the keyboard.

This case works quite differently and we need to add __an event listener__ for such purpose. An __event listener is an object__ that listens for an event to happen and __triggers a function__ every time it occurs.

Let's see the code before going on any further:

```jsx
useEffect(() => {
   document.addEventListener('keydown', handleKeyDown);
   return () => document.removeEventListener('keydown',handleKeyDown);
   }, [screenValue]);
```
The event for which the listener waits is a __keyboard key being pressed__. To specify that behavior, we're passing in the ___keydown___ __event type__.

When this event occurs, the function `handleKeyDown` will be called and its code will be executed.

Note that we're specifying this event listener within the ___useEffect___ __hook__, which, in addition, is being called __conditionally__.

__Ok, but... Why?__ Well, because we need this function __to be triggered__ every time the __screen value changes__. That's it 🙂

Oh, and __don't forget to remove the event listener to avoid odd behaviors__ in your code.

Let's now take a look at handler for the event:

```jsx
const handleKeyDown = e => {
   if (e.key === 'Backspace') {
      e.preventDefault();
      clearLastDigit();
   }
}
```
Note that the handler itself calls another function, which is the one executed to __delete the last entered digit__:

```jsx
const clearLastDigit = () => {
   if (screenValue !== "0")
      if (screenValue.length > 1)
         setScreenValue("0");
      else {
         setScreenValue(screenValue.substring(0, screenValue.length - 1));
      }
   }
```

This function, as mentioned right up above, __deletes the last entered digit of the screen value__ if its length is __greater than 1__. __Otherwise__, the screen value becomes __zero__ (the screen value __must never be empty__).

To carry out this deletion, we'll be calling __the substring method__ from 0 to the __current screen value length minus 1__.


##3.2. Numeric keys
The numeric keys are those keys __containing numbers to operate__ with.

Every time a numeric key is clicked, the following function is called:

```jsx
const handleClickNumericKey = value => {
   if (expectsOperand) {
      setScreenValue(String(value));
      setExpectsOperand(false);
   }
   else
      setScreenValue(screenValue === "0" ? String(value) : screenValue + value);
}
```

As done before, let's distinguish between these two scenarios:

__If the calculator is waiting for an operand__ (this means that there's an ongoing operation), the value we're introducing __will become the current screen value__ and we'll tell the calculator that __it doesn't need to wait for another operand__.

__If the calculator isn't waiting for a new operand__ (this means there is an ongoing operand which we can keep adding digits to), it just __appends the new digit to the current screen value if__ this one is __not zero__. __Otherwise__, the screen value __will be overwritten__. 

And, in which cases can the __screen value__ be __zero__? Well, when the calculator is __at initial state__, or __after cleaning__ the screen or the stored values, for example.

##3.3. Operator keys
The operator keys are those that represent __arithmetic operations__.

This is what happens when we click on an arbitrary operator:

```jsx
const handleClickOperator = operator => {
   const inputValue = parseFloat(screenValue);
   if (accValue === null) {
      setAccValue(inputValue);
   }
   else {
      if (currentOperator) {
         const resultValue = operate(currentOperator, accValue, inputValue);
      setAccValue(resultValue);
      setScreenValue(String(resultValue));
      }
   }
   setExpectsOperand(true);
   setCurrentOperator(operator);
}
```

###How this function works?
First things first. We need to __store the current screen value parsed to float__ in a constant so we can __operate__ with it.

Then, we'll check if we __already__ have an __accumulated value__ or not.

If there's __no accumulated value__ (we just entered the first operand), we'll __set the state__ for it to this new __input value__. 

>Bear in mind that the __accumulated value is only stored__ when an __operator__ is pressed, not when a __numeric key__ is pressed.

Else, if we __already__ have and __accumulated value__ and there's also an __operation going on__ (we just entered the second operand), then we can __operate__. After the proper operation is done, we'll __assign the result value to the accumulated value and the screen value__ (previously parsed to _string_ in this case).

In any case, we need to __store the new operator clicked__ for later and also __tell the calculator to wait for another operand__.

There you have the __operate function__:
```jsx
const operate = (operator, accValue, inputValue) => {
   switch (operator) {
      case "+": return accValue + inputValue;
      case "-": return accValue - inputValue;
      case "x": return accValue * inputValue;
      case "/": return accValue / inputValue;
      case "=": return inputValue;
   }
}
```
This function receives the __stored operator__, the __accumulated value__ and the __last screen value__ as parameters and, based on the operator value, a different __operation, which includes the other two values, is performed__ .

Really easy, right?

____

And that's pretty much it! I hope you found this tutorial useful and __don't hesitate to pose any doubts or questions you could have__ related to the tutorial and/or examples above.

____

🎉 Don't forget to follow @underscorecode on [Instagram](https://instagram.com/underscorecode) and [Twitter](https://twitter.com/underscorecode) for more daily webdev content 🖥🖤

____

####And last but not least... A quick friendly reminder before we go 😊
We all know there are million ways to get things done when it comes to programming and development, and we're here to __help and learn__, so, if you know another possible way to do what others are sharing (__not better, not worse, just different__), feel free to share it if you feel like it, but, please, __always be kind and respectful__ with the author and the rest of the community. Thank you!