DEV Community

Juliana Hassan
Juliana Hassan

Posted on

How i built a calculator using React js useReducer

React js is a javascript library for building awesome ui.
In this blog post, i would be sharing how i was able to build a simple calculator using the useReducer Hook, useState hook.

Setting up the Application.

To bootstrap the application we use create react app.

npx create-react-app react-calculator
Enter fullscreen mode Exit fullscreen mode

The second thing to do is to remove the boilerplate code and install the necessary dependencies

npm i react-router-dom helmet
Enter fullscreen mode Exit fullscreen mode

i wanted my app to have a good seo ranking so i install helmet.
i also wanted to be able to have different routes hence the need for react router dom.
i set up error boundary on my code to ensure that even if my app had an error it would not entirely break.
any route that is not accounted for would fall into the catch all route or the error page.
here is a code snippet of how my main.jsx looks like

import Counter from "./Counter";
import { Route, Routes } from "react-router-dom";
import Error from "./Error";
import { Helmet } from "react-helmet";
import ErrorBoundary from "./ErrorBoundary";

function App() {
  return (
    <>
      <Helmet>
        <title>reduce counter</title>
        <meta name="description" content="reducer counter" />
      </Helmet>
      <ErrorBoundary>
        <Routes>
          <Route path="/" element={<Counter />} />
          <Route path="*" element={<Error />} />
        </Routes>
      </ErrorBoundary>
    </>
  );
}

export default App;
Enter fullscreen mode Exit fullscreen mode
import { useReducer, useState } from "react";
import { reducer, initialState } from "./countReducer";
import React from "react";
import {
  INC_COUNT,
  DEC_COUNT,
  RESET_COUNT,
  ADD_VALUE,
  REDUCE_VALUE,
} from "./types";
import "./counter.css";

const Counter = () => {
  const [state, dispatch] = useReducer(reducer, initialState);
  const [input, setInput] = useState(+0);
  const data = (e) => {
    setInput(+e.target.value);
  };
  if (input >= 10000) {
    throw new Error("value is too big");
  }
  return (
    <div className="App">
      <div className="container">
        <div className="count-bar">
          <label htmlFor="" className="count-bar-lebel">
            total
          </label>
          <p>{state.number}</p>
        </div>
        <div className="count-text">
          <label htmlFor="" className="input-label">
            add any number
          </label>
          <input
            type="number"
            onChange={data}
            className="count-input"
            placeholder="enter any number"
            value={input}
          />
        </div>

        <div className="values">
          <button
            onClick={() => {
              dispatch({ type: ADD_VALUE, payload: input });
              setInput(0);
            }}
          >
            add any number
          </button>
          <button
            onClick={() => {
              dispatch({ type: REDUCE_VALUE, payload: input });
              setInput(0);
            }}
          >
            remove any number
          </button>
        </div>
        <div className="values">
          <button onClick={() => dispatch({ type: INC_COUNT })}>
            add (+1){" "}
          </button>
          <button onClick={() => dispatch({ type: DEC_COUNT })}>
            reduce (-1)
          </button>
        </div>
        <div className="reset">
          <button onClick={() => dispatch({ type: RESET_COUNT })}>
            reset (0){" "}
          </button>
        </div>
        <p className="warning">numbers greater than 10000 throws an error</p>
      </div>
    </div>
  );
};

export default Counter;
Enter fullscreen mode Exit fullscreen mode

The first thing i did when creating this component was to import both useState and useReducer from react,
then imported initialState and the reducer function from countReducer.
useRreducer takes in both the reducer function and the initialState.
state and dispatch are destructured from the useReducer hook.
I created my action types from a seperate file to aviod error.
The app has different action types namely

  • INC_COUNT,
  • DEC_COUNT,
  • RESET_COUNT,
  • ADD_VALUE,
  • REDUCE_VALUE,

INC_COUNT is responsible for adding one to the state value.
DEC_COUNT is responsible for removing one from the state value.
RESET_COUNT is responsible for resetting the count value to zero.

ADD_VALUE adds any desired value to the state.

REDUCE_VALUE removes any amount from the state

How the logic works

whenever any of the buttons are clicked, there is an onClick handler that contains the dispatch function that is triggered

this is how the reducer function looks like

import {
  INC_COUNT,
  DEC_COUNT,
  RESET_COUNT,
  ADD_VALUE,
  REDUCE_VALUE,
} from "./types";

export const initialState = {
  number: 0,
};

export const reducer = (state, action) => {
  switch (action.type) {
    case INC_COUNT:
      return {
        ...state,
        number: state.number + 1,
      };

    case DEC_COUNT:
      return {
        ...state,
        number: state.number - 1,
      };
    case RESET_COUNT:
      return {
        number: 0,
      };
    case ADD_VALUE:
      return {
        ...state,
        number: state.number + action.payload,
      };
    case REDUCE_VALUE:
      return {
        ...state,
        number: state.number - action.payload,
      };
  }
};
Enter fullscreen mode Exit fullscreen mode

Top comments (0)