A Simple Counter App using React.JS 
Published: 2024-03-13 08:54:44



![counter app with increment, decrement and reset buttons](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/p0g7hlh9ddogxqclta5r.png)




#**Table of Content**
> - Introduction
> - Project overview
> - Getting Started
> - Integrating the Counter Page
> - Error Boundary page
> - Custom 404 Page
> - About Page
> - Conclusion


#**Introduction**

Firstly let us see the meaning of custom hook. Custom Hook is a more advanced feature of React. It can let you reuse stateful logic without changing your component hierarchy. It will also make your component code more readable and easier to maintain.

##Task
Setup a custom counter hook with increment, decrement, reset, setValue functions with a valid UI. Implement a page for the custom hook, 404, and a page to test error boundary.



#**Project Overview**
I built this web application with some important features and implementation which include;

- An input field to set the counter to any starting value.
- Buttons that can automatically increase or decrease the counter by a value more or less than 1 when clicked.
-  A reset button that automatically set count back to 0
- Implementing a 404 Page, Error Boundary, and About Page.
- A clean and user friendly UI.
- Routing of pages.



#**Getting Started**

Before i began the project, i already had prior knowledge and was familiar with the basics of React Hooks and how to create a custom hook.

Firstly, i opened my terminal and i input the command `npm create-vite@latest` followed by the project name, i named mine **_My-Counter App_**.


##**Create react app**

```
npm create-vite@latest my-react-app
cd my-react-app 
npm install 
npm run dev
```


##**Install Dependencies** 

The next thing i did was to install the dependency  i will be needing for my project, `react router`, which will enables me to implement dynamic routing in my web app.


```
npm i react-router-dom
```



##**Setup**

In my newly created app i opened the src folder and i deleted the asset and App.css files.
Then i went ahead to create another folder in the src folder which i named Components, in the components folder i created 6 files named: `counter.jsx`,  `useCounter.jsx`, `errorBoundary.jsx`,  `notFound.jsx` and `about.jsx`.

Then i set up my `react-router` in the `App.jsx` file


```JavaScript
import { Routes, Route, NavLink } from "react-router-dom";
import Counter from "./components/Counter";
import About from "./components/About";
import NotFound from "./components/NotFound";
import TestError from "./components/TestError";
import './index.css'

function App() {
  return (
    <div className="App">
      <header className="nav-header">
        <NavLink className="counter-logo" to="/"><img src="./images/counter-logo.png" alt="logo" />
          Counter
        </NavLink>
        <nav>
          <NavLink className="nav-link" to="/test-error-boundary">Error Boundary</NavLink>
          <NavLink className="nav-link" to="/about">About</NavLink>
        </nav>
      </header>
      <main>
        <Routes>
          <Route path="/" element={<Counter />} />
          <Route path="/about" element={<About />} />
          <Route path="/test-error-boundary" element={<TestError />} />
          <Route path="*" element={<NotFound />} />
        </Routes>
      </main>
      <footer>
        <p className="text-align-center">
          Copyright &copy; 2024 Ayaoba. <br />
          All rights reserved.
        </p>
      </footer>
    </div>
  )
}

export default App;

```

#**Counter Page**


##**Implementing useCallback**

At the top of the `Counter.js.` file i imported the useCallback hook from React.

The React useCallback Hook will returns a memoized callback function. which will allows us to isolate resource intensive functions so that they will not automatically run on every render.



```JavaScript
import  { useCallback } from 'react'
import useCounter from '../components/UseCounter';

function Counter() {
    const { increment, decrement, reset, setValue, count } = useCounter();
  
    const handleSubmit = useCallback(
        (event) => {
            event.preventDefault();
            setValue(event.target.newValue.value);
            event.target.newValue.value = "";
        },
        [setValue]
    );

    return (
        <div className="counter">
        <h1>{count}</h1>
       <div className='counter-button'>
       <button className='counter-btn_plus button-shadow' onClick={() => increment()}><img src="./images/plus.png" alt="plus" /></button>
       <button className='counter-btn_reset button-shadow' onClick={() => reset()}><img src="./images/reset.png"  alt="reset" /></button>
       <button className='counter-btn_minus button-shadow' onClick={() => decrement()}><img src="./images/minus.png" alt="minus"/></button>
       </div>
            <form onSubmit={handleSubmit}>
          <input className='new-value' name="newValue" type="tel" placeholder="New value" />
          <button className="btn-set-value">Set value</button>
          </form>
        </div>
    );
  }
export default Counter;
```




##**Implementing useReducer**

In the `useCounter.jsx` file I implemented the UseReducer method. 
useReducer is a React hook that allows us to manage state in a simple and consistent way. This Hook accepts two arguments. It returns the current state and a dispatch method.



```JavaScript
import { useReducer, useCallback } from "react";

const useCounter = () => {
  const ACTIONS = {
    INCREASE: "increase",
    DECREASE: "decrease",
    RESET: "reset",
    SET_VALUE: "setValue",
  };
  function getValue(value, count) {
    let result = count;
    if (!value) {
        result = count;
    } else if (Number.isFinite(+value)) {
        result = +value;
    }
      return result;
    }

  function reducer(count, action) {
    switch (action.type) {
      case ACTIONS.SET_VALUE:
        return getValue(action.value, count);
      case ACTIONS.INCREASE:
        return count + 1;
      case ACTIONS.DECREASE:
        return count -1;
      case ACTIONS.RESET:
        return 0;
      default:
        return count;
    }
  }
  const [count, dispatch] = useReducer(reducer, 0);
  const increment = useCallback(() => {
    dispatch({ type: ACTIONS.INCREASE });
  }, [ACTIONS.INCREASE]);

  const decrement = useCallback(() => {
    dispatch({ type: ACTIONS.DECREASE });
  }, [ACTIONS.DECREASE]);

  const reset = useCallback(() => {
    dispatch({ type: ACTIONS.RESET });
  }, [ACTIONS.RESET]);

  const setValue = useCallback(
    (value) => {
      dispatch({ type: ACTIONS.SET_VALUE, value });
    },
    [ACTIONS.SET_VALUE]
  );

  return { count, increment, decrement, reset, setValue };
};

export default useCounter;
```



#**Error Boundary page**

I opened the  `errorBoundary.jsx` file and i created an Error boundary component to catch JavaScript errors anywhere in its child component tree, log the errors, and display a fallback UI instead of the component tree that crashed.


#**Custom 404 Page**

In `notfound.jsx` i  added a custom 404 page that will display when the server cannot find a page the user has requested.

#**About Page**

Finally, in `About.jsx` i added an About page that consist of relevant information on how to navigate though the `counter app`.


#**Conclusion**

This project was my second semester examination Project at [AltSchool Africa](https://www.altschoolafrica.com).

You can check out the [live site](https://abolarin-esther-counter-project.netlify.app/) of my counter app. 
And also, the [GitHub repo](https://github.com/AyaobaEstelle/counter-2.git) to the application.

Thanks for Reading.