DEV Community

Cover image for How to Create a Counter App with useReducer and Custom Hooks
Boma Sim-Jaja
Boma Sim-Jaja

Posted on

How to Create a Counter App with useReducer and Custom Hooks

Welcome to my very first post as a front-end engineering student. In this post, I will be building 2 different counter apps, one created with the useReducer Hook and the other with the custom hook.
Before proceeding you need to have basic knowledge of React and React Hooks.

Features of Both Counter Apps

  • Increment button
  • Decrement button
  • Reset button
  • Input field to set value

Setting Up

This project is built entirely in code sandbox, so no need to run create-react-app. Code sandbox already has react template set up, but if you are using an IDE like vscode then simply open up your terminal and run.npx create-react-app react-counter-app.
Under components create two pages, one for the useReducer counter app and the other for the custom hook counter app.
Now let's proceed ;)

useReducer Counter App

The useReducer hook is an alternative to the useState hook. It is most preferable when you are keeping track of multiple pieces of state that rely on complex logic. Just like other hooks in react we can;
import React, { useReducer} from 'react'

The useReducer hook takes in two arguments and returns an array with 2 values which are the state value and a dispatch function to which you can pass an action and later invoke it.

// Defining the initial state and the reducer
const initialState = { count: 0, value: parseInt('') };
const reducerCounter = (state, action) => {
  switch (action.type) {
    case 'increment':
      return { count: state.count + 1 };
    case 'decrement':
      return { count: state.count !== 0 ? state.count - 1 : (state.count = 0) };
    case 'setValue':
      return { count: action.payload };
    case 'reset':
      return initialState;

    default:
      throw new Error('Error occurred in counter');
  }
};
Enter fullscreen mode Exit fullscreen mode

I started off by defining the initial value and setting parseInt to an empty string. The parseInt function converts its first argument to a string, parses that string, and returns an integer or NaN. This function helps us extract the value from the input field after the user inputs it.

Next, we initialize the useReducer hook

// Initialising useReducer hook
export default function ReactCounter() {
  const [state, dispatch] = useReducer(reducerCounter, initialState);
  const changeCounterValue = (value) => {
    dispatch({ type: 'setValue', payload: parseInt(value) });
  };

  return (
    <>
      <div className="app">
        <div>
          <center className="header">
            <h1>UseReducer Counter</h1>
          </center>
          <div className="count">
            <h2>Count: </h2>
            <h1>{state.count}</h1>
          </div>
          <center>
            <input
              placeholder="Set Value"
              className="setValue"
              type="number"
              onChange={(e) => {
                changeCounterValue(e.target.value);
              }}
            />
          </center>
          <div className="buttons">
            <center>
              <button
                type="button"
                onClick={() => {
                  dispatch({ type: 'decrement' });
                }}
              >
                -
              </button>
              <button
                type="button"
                onClick={() => {
                  dispatch({ type: 'increment' });
                }}
              >
                +
              </button>
              <button
                type="button"
                className="reset-button"
                style={{ fontWeight: 900, fontSize: 20 }}
                onClick={() => {
                  dispatch({ type: 'reset' });
                }}
              >
                reset
              </button>
            </center>
          </div>
        </div>
      </div>
    </>
Enter fullscreen mode Exit fullscreen mode

Counter App with Custom Hooks

Custom hooks are reusable functions that we create ourselves as developers. They can be used to add special unique functionality to the react application. Custom hooks start with 'use' and can be used to call other hooks.
I started off by creating a custom hook called useCounter. Our useCounter hook would have a state to hold the counter value. It would have 4 methods; increment, decrement, reset, and set value.

const useCounter = (initialValue = 0) => {
  const [counter, setCounter] = useState(initialValue);

  const handleIncrease = () => setCounter((prev) => prev + 1);
  const handleDecrease = () => setCounter((prev) => prev - 1);
  const handleReset = () => setCounter(initialValue);
  const handleSetValue = (value) => setCounter(parseInt(value));

  return [
    counter,
    {
      setIncrease: handleIncrease,
      setDecrease: handleDecrease,
      setReset: handleReset,
      setValue: handleSetValue
    }
  ];
};

function CustomHook() {
  const [count, { setIncrease, setDecrease, setReset, setValue }] = useCounter(
    0
  );

  return (
    <section>

      <div className="app">
        <div>
          <center className="header">
            <h1>Custom Hook Counter</h1>
          </center>
          <div className="count">
            <h2>Count is: {count}</h2>
          </div>
          <center>
            <input
              placeholder="Set Value"
              className="setValue"
              type="number"
              onChange={(e) => {
                setValue(e.target.value);
              }}
            />
          </center>
          <div className="buttons">
            <button onClick={setIncrease}>+</button>
            <button onClick={setDecrease}>-</button>
            <button
              onClick={setReset}
              style={{ fontWeight: 900, fontSize: 20 }}
            >
              Reset
            </button>
          </div>
        </div>
      </div>
    </section>
  );
}
Enter fullscreen mode Exit fullscreen mode

And there you have it, two counter apps, one with useReducer and the other with custom hooks.

Additional Feature

Here are some of the additional features I added to the app.

  • Randomized Background Color
  • 404 page
  • A page to test error boundary and
  • I implemented SEO

View full code here - https://codesandbox.io/s/counter-app-jyq7n9?file=/src/Home.js

Visit the published site here - https://csb-jyq7n9.netlify.app/

Top comments (0)