DEV Community

Cover image for Frontend Test: How I Built a Multi-functional Calculator that Fetches API Data
Stanlisberg
Stanlisberg

Posted on

Frontend Test: How I Built a Multi-functional Calculator that Fetches API Data

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:

npm create vite@latest
Enter fullscreen mode Exit fullscreen mode

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

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;

Enter fullscreen mode Exit fullscreen mode

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

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;

Enter fullscreen mode Exit fullscreen mode

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 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

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;

Enter fullscreen mode Exit fullscreen mode

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

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;

Enter fullscreen mode Exit fullscreen mode

App
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

@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;
}

Enter fullscreen mode Exit fullscreen mode

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

calculator image

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
The source code: Link

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 || Linkedin || Twitter

Top comments (0)