How to Build a Custom Counter App With Error Boundary and 404 page in React.

One of the most well-liked front-end JavaScript libraries is React. React is widely used by businesses to create their user interfaces, and it has grown in popularity among programmers.

React makes it simple to create simple programs like this straightforward counter application. Starting with a straightforward tutorial will help you grasp some of React's basic but important concepts.

Features of the Counter App

In this project, you are going to develop a counter application using the use reducer and with the following features:

  1. Increment Button: This will increase the count by one.

  2. Decrement Button: This will decrease the count by one.

  3. Reset Button: This will set the count to zero.

  4. Set Value: This will set the count to the input value.

Basic Concepts of React

Before continuing, you should comprehend some of the fundamental React ideas that you will be using in this project:

  1. Components: The fundamental building pieces of React applications are components. They have separate, reusable code. You can divide the user interface into distinct sections by using components. These components can then be used again and independently used.

  2. State: In React, you may use an object to store information describing a component's state. Components can now maintain and update their own data thanks to this. The state of a component determines how it renders and behaves.

  3. Functional Components: The functional component in React is just a JavaScript function that takes an argument called props and returns a React element (JSX).

  4. Props: You can transmit data from a parent component to a child component by using props, which is short for "properties." One of the essential components of React is props, and you can use props to carry out a variety of activities in React.

  5. Hooks: React has built-in functions called "hooks" that let you control state and other React features like lifecycle methods inside of functional components. You can use them to write clear and concise code. Soon, you'll learn how to use the useState() hook to handle state.

Step 1: Setting Up the Project

Start by launching your terminal and entering the upcoming command:

npx create-react-app my-counter-app

With a new react app created, you can begin working on your project. A file system structure with numerous files and folders will be generated.

To launch the development server, enter the following command into the terminal:

npm start

Your browser should launch a new tab with the address http://localhost:3000 after you enter that command. You can make changes to the project at any time, and they will all be automatically updated here.

Step 2: Building the Counter Application's Skeleton.

The default code that is present in the src/App.js file should be deleted. Use the following code to build the application's skeleton:

import { useRef, useState } from "react";
import { useErrorHandler } from "react-error-boundary";

const MAX_COUNT_ALLOWED = 5;

export default function ErrorBoundary() {
  //useState for count...
  const [state, setState] = useState(0);
  const [history, setHistory] = useState('');

  //initializing errorHandler
  const HandleError = useErrorHandler();

  //increament, decreament and reset functions...
  if (state > MAX_COUNT_ALLOWED) {
    throw new Error("Exceeded count");
  }
  const Increament = () => {
    try {
      if (state === MAX_COUNT_ALLOWED) {
        throw new Error("Exceeded count");
      } else {
        setState(state + +1);
      }
    } catch (e) {
      HandleError(e);
    }
  };
  const Decreament = () => {
    setState(state - 1);

  };
  const Reset = (prev) => {
    if (prev === 0) {
      setHistory(0)
    }else{
      setHistory(state)
    }
    setState((prev = 0));
  };

  const inputRef = useRef(null);

  function handleClick() {
    setState(inputRef.current.value - 0);
  }
  //display
  return (
<section className="main_container">
      <div>
        <p>
          <input ref={inputRef} type="number" id="message" name="message" />
          <button onClick={handleClick}>set value</button>
        </p>
        <div className="counter_label">Count: {state}</div>
        <button className="Action-btn" onClick={Increament}>
          Increament
        </button>
        <button className="Action-btn" onClick={Decreament}>
          Decreament
        </button>
        <button className="Action-btn" onClick={Reset}>
          reset
        </button>
        <p>Your last count beofore you reset was: {history}</p>          
      </div>
    </section>
  );
}

The useState hook from the react module is imported in the first line of code. Create the count state using it, and set its initial value to 0. The setCount function allows you to modify the count's value.

The useRef hook from the react module is imported in the second line of code. It allows you to persist values between renders. It can be used to store a mutable value that does not cause a re-render when updated. It can be used to access a DOM element directly. but in this case we used it to access the input value after we initialized it.

const inputRef = useRef(null);
<input ref={inputRef} type="number" id="message" name="message" />

The useErrorHandler from the react-error-boundary module. This means we can now use error boundaries to catch errors during API requests, event handlers, and other parts of code where errors could occur. but for the sake of this project, we want to catch an error in the event handler. (this app will show an error when the count is over the count limit which we set to 5.

const MAX_COUNT_ALLOWED = 5;

this is used to throw error if count exceeds count limit (5)

if (state > MAX_COUNT_ALLOWED) {
    throw new Error("Exceeded count");
  }

note: we wrapped the entire application within the error boundary in the App.js file and also we have a FallbackComponent which is the display page that pops up when an error is caught within the components wrapped inside the error boundary, in this case our entire app is wrapped in the errorboudary.

        <ErrorBoundary FallbackComponent={Fallback} onError={errorHandler}>
          <Navigation />
          <Routes>
            <Route path="/" element={<Home />} />
            <Route path="/Error" element={<Error />} />
          </Routes>
        </ErrorBoundary>

note: the code below displays the 404 page when the wrong paths are added.

<Route path="/*" element={<Page404 />} />

You'll use the increment Count, decrement Count, set value and reset Count functions later to increase, decrease, set value and reset the value of the counter.

I added an extra function which is the last count before reset ( count history). the gives us the value of the count value before reset everytime reset is clicked. and the code below explains how we did that.

const Reset = (prev) => {
    if (prev === 0) {
      setHistory(0)
    }else{
      setHistory(state)
    }
    setState((prev = 0));
  };

You may have noticed that the count variable in the markup is surrounded by curly brackets { }. In essence, this instructs the JSX parser to treat the text contained inside those braces as JavaScript.

Step 3: Adding the Functionality to the Counter Application

To implement the functionality of the counter application, you must create four buttons: a decrement button, an increment button, set value and a reset button. Inside the buttons div, insert the following code:

      <div>
        <p>
          <input ref={inputRef} type="number" id="message" name="message" />
          <button onClick={handleClick}>set value</button>
        </p>
        <div className="counter_label">Count: {state}</div>
        <button className="Action-btn" onClick={Increament}>
          Increament
        </button>
        <button className="Action-btn" onClick={Decreament}>
          Decreament
        </button>
        <button className="Action-btn" onClick={Reset}>
          reset
        </button>
        <p>Your last count beofore you reset was: {history}</p>          
      </div>

The decrement, increment, set value(handleclick) and reset functions will be activated when you click these buttons. The title and action props are being passed from the parent App component to the child button component.

Update these functions in the App.js file with the following code:

const increment = () => {
  setCount(count + 1);
};

const decrement = () => {
  setCount(count - 1);
};

const Reset = (prev) => {
    if (prev === 0) {
      setHistory(0)
    }else{
      setHistory(state)
    }
    setState((prev = 0));
  };
function handleClick() {
    setState(inputRef.current.value - 0);
  }

The setCount function will update the state of the count.

The setHistory function will update the state of the history.

The Error.js file's final code will appear as follows:

import { useRef, useState } from "react";
import { useErrorHandler } from "react-error-boundary";

const MAX_COUNT_ALLOWED = 5;

export default function ErrorBoundary() {
  //useState for count...
  const [state, setState] = useState(0);
  const [history, setHistory] = useState('');

  //initializing errorHandler
  const HandleError = useErrorHandler();

  //increament, decreament and reset functions...
  if (state > MAX_COUNT_ALLOWED) {
    throw new Error("Exceeded count");
  }
  const Increament = () => {
    try {
      if (state === MAX_COUNT_ALLOWED) {
        throw new Error("Exceeded count");
      } else {
        setState(state + +1);
      }
    } catch (e) {
      HandleError(e);
    }
  };
  const Decreament = () => {
    setState(state - 1);

  };
  const Reset = (prev) => {
    if (prev === 0) {
      setHistory(0)
    }else{
      setHistory(state)
    }
    setState((prev = 0));
  };

  const inputRef = useRef(null);

  function handleClick() {
    setState(inputRef.current.value - 0);
  }
  //display
  return (
    <section className="main_container">
      <div>
        <p>
          <input ref={inputRef} type="number" id="message" name="message" />
          <button onClick={handleClick}>set value</button>
        </p>
        <div className="counter_label">Count: {state}</div>
        <button className="Action-btn" onClick={Increament}>
          Increament
        </button>
        <button className="Action-btn" onClick={Decreament}>
          Decreament
        </button>
        <button className="Action-btn" onClick={Reset}>
          reset
        </button>
        <p>Your last count beofore you reset was: {history}</p>          
      </div>
    </section>
  );
}

The App.js file's final code will appear as follows:

import React from "react";
import { ErrorBoundary } from "react-error-boundary";
import { Fallback } from "./components/Fallback";
import { BrowserRouter, Routes, Route } from "react-router-dom";
import Home from "./components/Home";



function App() {
  const errorHandler = (error, errorInfo) => {
    console.log("logging", error, errorInfo);
  };

  return (
    <section>
      <BrowserRouter>
        <ErrorBoundary FallbackComponent={Fallback} onError={errorHandler}>
          <Routes>
            <Route path="/" element={<Home />} />
            <Route path="/Error" element={<Error />} />
          </Routes>
        </ErrorBoundary>
      </BrowserRouter>
    </section>
  );
}

export default App;

The Best React Practices to Follow

React code can be written in a variety of ways, but it should always be organized as clearly as possible. This will make maintenance simple for you and help your application run more efficiently overall.

You can adhere to a number of React best practices, such as avoiding repetitive code, creating tests for each React component, using object destructuring for props, and adhering to naming conventions.