Frontend Test: How I Built a Multi-functional Calculator that Fetches API Data
Tags: tutorial, frontend, react, webdev
Published: 2023-11-10 07:40:33

As a junior front-end developer aiming for your first job, it's likely you'll have to do coding challenges as part of the hiring process. These tests help employers assess your skill level and choose the best candidates that are suited for the job. I've solved various challenges, like converting Figma designs into picture-perfect code replicas, working with APIs, writing algorithms, and building landing pages. These experiences have improved my skills and taught me better approaches to coding tests in general.

In this guide, I will walk you through how I built a simple calculator app that does basic arithmetics operations and also fetches data from an API with react and vanilla css for a coding challenge. Upon completion, you will have the skills to make similar apps with ease and enhance your API skills in coding tasks.

**App Overview** 
This application performs basic arithmetic operations like addition, subtraction, multiplication, and division. It features a toggle button for switching between a serious and fun mode. In serious mode, it functions as a regular calculator, while in fun mode, it fetches and displays data from different APIs when specific codes are typed. These APIs include a movies API, a jokes API, and a memes API, which are triggered by inputting corresponding codes(753 for movies, 172 for jokes, and 112 for memes) into the calculator.

Let's proceed with the build-up process. Shall we?
Hold on! Since this is a react application, it is imperative to have some basic requirements like:

- Understanding of react

- A code editor

- A web browser

- Basic knowledge of working with API

**Build-up Process**
To set up a React boilerplate, open your VSCode terminal or command prompt and enter the following command:

```html
npm create vite@latest
```
You'll be prompted to provide the app's name and choose additional dependencies. It's recommended to keep the setup simple since this is a small project.

Now, this app consists of two components: the **SeriousPage** and **FunPage** components. The **SeriousPage** hosts the main calculator for basic arithmetic operations, while the **FunPage** component handles and interprets data fetched from the API, all within a structure similar to that of the SeriousPage component.

Let's create the SeriousPage component.
**SeriousPage**

```javascript 
import React, { useState } from "react";

function SeriousPage() {
  const [answer, setAnswer] = useState("");

  const clickButton = (e) => {
    setAnswer(answer.concat(e.target.value));
  };

  const clearButton = () => {
    setAnswer("");
  };

  const backSpace = () => {
    setAnswer(answer.slice(0, -1));
  };

  const evaluate = () => {
    setAnswer(eval(answer).toString());
  };

  return (
    <>
      <div className="div-container">
        <input
          type="text"
          value={answer}
          onChange ={() => {}}
        />
        <div className="button">
          <button
            className="change-color"
            id="clear-button"
            onClick={clearButton}
          >
            Reset
          </button>
          <button className="change-color" id="back-space" onClick={backSpace}>
            Del
          </button>
          <button className="change-color" value="/" onClick={clickButton}>
            &divide;
          </button>
          <button value="7" onClick={clickButton}>
            7
          </button>
          <button value="8" onClick={clickButton}>
            8
          </button>
          <button value="9" onClick={clickButton}>
            9
          </button>
          <button className="change-color" value="*" onClick={clickButton}>
            &times;
          </button>
          <button value="4" onClick={clickButton}>
            4
          </button>
          <button value="5" onClick={clickButton}>
            5
          </button>
          <button value="6" onClick={clickButton}>
            6
          </button>
          <button className="change-color" value="-" onClick={clickButton}>
            -
          </button>
          <button value="1" onClick={clickButton}>
            1
          </button>
          <button value="2" onClick={clickButton}>
            2
          </button>
          <button value="3" onClick={clickButton}>
            3
          </button>
          <button className="change-color" value="+" onClick={clickButton}>
            +
          </button>
          <button value="0" onClick={clickButton}>
            0
          </button>
          <button value="." onClick={clickButton}>
            .
          </button>
          <button className="change-color" id="answer" onClick={evaluate}>
            =
          </button>
        </div>
      </div>
    </>
  );
}

export default SeriousPage;

```

From the provided code snippet, we have a state named `answer` and several associated functions, including `clickButton`, `clearButton`, `backSpace`, and `evaluate`. Here's an explanation of each:

- `answer`: This state stores all button values and the final result of the calculator.

- `clickButton`: This function is responsible for handling the logic when a button is clicked in the calculator interface and stores its value in the answer state.

- `clearButton`: This function sets the state to an empty string; this is what happens when the Reset button is clicked.

- `backSpace`: The function uses the array slice method to remove the last input value entered in the calculator; this is what happens when the Del button is clicked.

- `evaluate`: This function processes the final input and performs the necessary arithmetic calculation to generate the result displayed on the calculator.

Before creating our **FunPage** component, it's necessary to set up a **Context API** to manage the states for the API data that will be used within the component.

**CalculatorContext**

```javascript
import { createContext, useState } from "react";

const CalculatorContext = createContext();

export const CalculatorProvider = ({ children }) => {
  const [movieData, setMovieData] = useState();
  const [jokeData, setJokeData] = useState(); 
  const [memeData, setMemeData] = useState();
  const getRandom = Math.floor(Math.random() * 10);

  const fetchResponse = async (value) => {
    if (value === 753) {
      const options = {
        method: "GET",
        headers: {
          accept: "application/json",
          Authorization:
            "Bearer XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX",
        },
      };
      //-------Fetch Movies-----------
      const response = await fetch(
        "https://api.themoviedb.org/3/discover/movie?page=${page}",
        options
      );
      const data = await response.json();
      const { results } = data;

      setMovieData(results[getRandom].original_title);

    } else if (value === 172) {
      //-------Fetch Jokes------------
      const response = await fetch(
        "https://v2.jokeapi.dev/joke/Programming?type=single&amount=10"
      );
      const data = await response.json();
      const { jokes } = data;

      setJokeData(jokes[getRandom].joke);

    } else if (value === 112) {
      //-------Fetch Memes------------
      const response = await fetch("https://api.imgflip.com/get_memes");
      const item = await response.json();
      const { data } = item;

      setMemeData(data.memes[getRandom].name);
    }
  };

  return (
    <CalculatorContext.Provider
      value={{
        fetchResponse,
        movieData,
        jokeData,
        memeData
      }}
    >
      {children}
    </CalculatorContext.Provider>
  );
};

export default CalculatorContext;

```
Within this context, we declared a function named `fetchResponse`, which takes an argument named `value`. This argument is utilized in our conditional statement to facilitate our API requests based on their corresponding codes:

- 753: Fetch movie data

- 172: Fetch jokes data

- 112: Fetch meme data

In order to manage and store the data fetched from the APIs, we will make use of the states; `movieData`, `jokeData`, and `memeData`. Since each API call returns 100 data, we created a variable `getRandom` to ensure that the fetched data is within a random integer range between 0 and 9 using the `Math.random()` method. This allows each API call to return a random data point between 0 and 9.

> _Note: For the movie API, you will require an API key to access the API. Kindle visit [this link](https://developer.themoviedb.org/docs) to register and obtain your API key_

In the **FunPage** component, we will put the pieces together and leverage the `Context API`. This `Context API` allows us to seamlessly access the API data within the component and also enables us to manipulate and interpret the data effectively within the user interface (UI).

**FunPage**

```javascript
import React, { useState, useEffect, useContext } from "react";
import CalculatorContext from "./CalculatorContext";

function FunPage() {
  const [answer, setAnswer] = useState("");
  const { displayResponse, movieData, jokeData, memeData } = useContext(CalculatorContext);

  useEffect(() => {
    displayResponse(753);
    displayResponse(172);
    displayResponse(112);
  }, []);

  const clickButton = (e) => {
    setAnswer(answer.concat(e.target.value));
  };

  const backSpace = () => {
    setAnswer(answer?.slice(0, -1));
  };

  const evaluate = () => {
    if (answer?.includes("753")) {
      setAnswer(movieData);
    } else if (answer?.includes("172")) {
      setAnswer(jokeData);
    } else if (answer?.includes("112")) {
      setAnswer(memeData);
    }
  };

  return (
    <>
      <div className="div-container">
        <input type="text" value={answer} onChange={() => {}} />
        <div className="button">
          <button
            className="change-color"
            id="clear-button"
            onClick={() => {
              setAnswer("");
              displayResponse(753);
              displayResponse(172);
              displayResponse(112);
            }}
          >
            Reset
          </button>
          <button className="change-color" id="back-space" onClick={backSpace}>
            Del
          </button>
          <button
            className="change-color"
            value="/"
            disabled={true}
            onClick={clickButton}
          >
            &divide;
          </button>
          <button value="7" onClick={clickButton}>
            7
          </button>
          <button value="8" onClick={clickButton}>
            8
          </button>
          <button value="9" onClick={clickButton}>
            9
          </button>
          <button
            className="change-color"
            value="*"
            onClick={clickButton}
            disabled={true}
          >
            &times;
          </button>
          <button value="4" onClick={clickButton}>
            4
          </button>
          <button value="5" onClick={clickButton}>
            5
          </button>
          <button value="6" onClick={clickButton}>
            6
          </button>
          <button
            disabled={true}
            className="change-color"
            value="-"
            onClick={clickButton}
          >
            -
          </button>
          <button value="1" onClick={clickButton}>
            1
          </button>
          <button value="2" onClick={clickButton}>
            2
          </button>
          <button value="3" onClick={clickButton}>
            3
          </button>
          <button
            disabled={true}
            className="change-color"
            value="+"
            onClick={clickButton}
          >
            +
          </button>
          <button value="0" onClick={clickButton}>
            0
          </button>
          <button value="." onClick={clickButton}>
            .
          </button>
          <button className="change-color" id="answer" onClick={evaluate}>
            Send
          </button>
        </div>
      </div>
    </>
  );
}

export default FunPage;

```
If you noticed from the code above, the component is set up with an almost similar structure to that of the **SeriousPage** component because we are basically switching from one component to the other while maintaining the same user interface. This component features:

- The `useContext` hook that allows us to access all the states and functions managed within the Context API.

- The `evaluate` Function that comprises code blocks that store the API data in our `answer` state when certain conditions are satisfied.

- The `useEffect` hook that wraps all function calls while utilizing an empty dependency array `[]` to ensure it runs once and manages any side effects. It's important to note that we passed three parameters, 753, 172, and 112, to the dailyResponse function call, representing the `value` argument used as a condition in the dailyResponse function declaration within our Context API. 

As you can observe, our application is gradually coming together. Let's render the components inside our app component and visualize the final output in the user interface.

**App Component**

```javascript
import { useState } from "react";
import SeriousPage from "./components/SeriousPage";
import FunPage from "./components/FunPage";
import { CalculatorProvider } from "./components/CalculatorContext";
import { Switch } from "antd";

function App() {
  const [switchState, setSwitchState] = useState("Serious");
  const [changeComponent, setChangeComponent] = useState(true);

  //---- For switch-----
  const onChange = (checked) => {
  console.log(`switch to ${checked}`);

    // ---Disable button when switched to fun
    if (checked === true) {
      setSwitchState("Fun");
      setChangeComponent(false);
    } else if (checked == false) {
      setSwitchState("Serious");
      setChangeComponent(true);
    }
  };
  return (
    <>
      <div className="app">
        <div className="switch">
          <p className="cac-name">My Calc</p>
          <Switch
            size="default"
            onChange={onChange}
            className="switch-button"
          />
        </div>
        <div className="switch-state">{switchState}</div>
        <CalculatorProvider>
          {changeComponent === false ? <FunPage /> : <SeriousPage />}
        </CalculatorProvider>
      </div>
    </>
  );
}

export default App;

```


![App](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/j3bib18x1agfi8cwi04a.png)
A visual representation of the app component.

In this component, we performed the following task: 

- Imported the following components and packages: **CalculatorProvider**, **SeriousPage** component, **FunPage** component, and a toggle package from Ant Design.

- Declared a function named `onChange` and two states named `switchState` and `changeComponent`

Within the `onChange` function, we implemented a conditional statement to display the "Serious" or "Fun" word at the top right of the calculator based on the toggle's status. Additionally, we wrapped our components within the **CalculatorProvider**, ensuring that the provided context values are accessible to the components in the component tree.

Furthermore, inside the **CalculatorProvider**, we declared a ternary operator to conditionally render each component(**FunPage** or **SeriousPage**), depending on whether the `changeComponent` state is true or false.

> _Note: For the third-party Ant Design toggle package, you can use the following command to install it:
> `npm install ant-d`_

For the styling
**CSS**
```Css
@import url("https://fonts.googleapis.com/css2?family=Poppins:wght@100;200;300;400;500;600;700;800;900&display=swap");

:root {
  font-family: Inter, system-ui, Avenir, Helvetica, Arial, sans-serif;
  line-height: 1.5;
  font-weight: 400;

  color-scheme: light dark;
  background: whitesmoke;
  color: #242424;

  font-synthesis: none;
  text-rendering: optimizeLegibility;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  -webkit-text-size-adjust: 100%;
}

* {
  box-sizing: border-box;
  margin: 0;
  padding: 0;
  font-family: "poppins", sans-serif;
  font-size: 18px;
}

body {
  display: flex;
  justify-content: center;
  align-items: center;
}

.div-container {
  margin-top: 10px;
}

.switch {
  display: flex;
  justify-content: space-between;
  text-align: end;
  margin-top: 70px;
}

.container {
  top: 50%;
  left: 50%;
  border-radius: 6px;
  position: absolute;
  margin: 0 auto;
  width: 256px;
  text-align: center;
  border: 10px solid #101116;
  background: #101116;
  transform: translate(-50%, -50%);
}

.button {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  /* grid-auto-rows: minmax(60px, auto); */
}

button {
  height: 50px;
}
input[type="text"] {
  height: 150px;
  width: 350px;
  font-size: 15px;
  font-weight: 600;
  letter-spacing: 2px;
  background-color: #101116;
  border: 1px solid grey;
  border-radius: 5px;
  color: #fff;
  text-align: right;
  margin: 5px auto;
  padding: 0 20px 0 20px;
}

.switch-state,
.cac-name {
  text-align: end;
  font-weight: bold;
}

#clear-button {
  grid-column: 1/3;
  grid-row: 1;
  color: #262834;
}

#answer {
  grid-column: 3/5;
  grid-row: 5;
  color: #262834;
}

#back-space {
  color: #262834;
}

button {
  margin: 7px;
  border: none;
  border-radius: 5px;
  background: #262834;
  cursor: pointer;
  color: #ffff;
}

button:focus,
button:active,
input:focus,
input:active {
  outline: none;
}

.change-color {
  background-color: #afaba5;
}

```
Now, it's time to see the final result of our creation.
**Final Result**


![calculator image](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/xh4z2qujvqckyurzk6ja.gif)

The demo snippet provides an overview of all functionalities of our application after completion. In the demonstration, when the toggle is switched to the funPage, it handles arithmetic operations, while the seriousPage retrieves data from the API and showcases it on the calculator's screen.

**Conclusion**
Building this application has demonstrated the seamless integration of basic arithmetic functionalities of a calculator and API data fetching, enhancing the user experience and introducing an aura of fun. By implementing the toggle feature, users can swiftly switch between serious and fun modes, allowing efficient and easy engagement.

The implementation of various APIs, such as the movies, jokes, and memes APIs, has not only enhanced the application's structure but has also showcased the versatility of modern web development. Following the comprehensive steps outlined in this guide, developers can easily build similar applications that combine functionality with fun hence, acing technical tasks and catering to specific needs.

Check out the live version: [calculator-test-sooty.vercel.app](calculator-test-sooty.vercel.app)
The source code: [Link](https://github.com/Stanlisberg/Calculator-test)

If you find this article interesting, please consider liking and sharing it. Additionally, I welcome your thoughts and feedback in the comment section below. Thank you for your engagement!

Open to work🙂
Did you enjoy this article or need an experienced Technical Writer / React Developer for a remote, full-time, or contract-based role? Feel free to contact me.
[Github](github.com/stanlisberg) || [Linkedin](https://www.linkedin.com/in/stanley-nnabugo-ba77b7260) || [Twitter](twitter.com/bubuTheCoder)