A React Counter App Setup Implementing Custom Hooks with useReducer On Vercel With Good SEO.

For the second-semester examination project, AltSchool Africa required its student to choose from a list of options and make project.

From the options given, I chose to work on a counter app using ReactJs framework. Based on this, the project chosen is required to have the following:

  • A custom hook with increment, decremet, reset and setValue functions.

  • A combination of states with useReducer implementing a counter with the features of the custom hook (increment, decrement, reset, setValue).

  • Implement a page for custom hook, useReducer, 404 page and a page to test error boundary.

  • A valid UI.

  • Good SEO.

First step was to install react app npx create-react-app alt-exam. Once the installation process was completed, I proceeded to delete files that were irrelevant to the project being built.

I implemented SEO by installing React Helmet Async npm i react-helmet-async and importing Helmet into my pages. I also installed React Router Dom npm i react-router-dom. Afterwards I imported BrowserRouter from react-router-dom and HelmetProvider from react-helmet-async into index.js.

import { BrowserRouter } from "react-router-dom";
import { HelmetProvider } from "react-helmet-async";

const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
<BrowserRouter>
    <React.StrictMode>
      <HelmetProvider>
        <App />
      </HelmetProvider>
    </React.StrictMode>
  </BrowserRouter>
);

I went ahead to build up the UI of the counter app. Afterwards, I created a useCounter file for the custom hook.

import { useReducer } from "react";
import { reducer } from "../reducer/reducer";

function useCounter() {
    const initialState = { count: 0, value: parseInt("") };
    const [state, dispatch] = useReducer(reducer, initialState);


    function increment() {
        return dispatch({ type: "increment" });
    }

    function decrement() {
        return dispatch({ type: "decrement" });
    }

    function reset() {
        return dispatch({ type: "reset" });
    }

    function setValue(value) {
        return dispatch({ type: "setValue", payload: parseInt(value) });
    }
    return { state, increment, decrement, reset, setValue };
}

export default useCounter;

i created a separate file for useReducer.


export const reducer = (state, action) => {
  switch (action.type) {
    case "setValue":
      return { count: action.payload };
    case "increment":
      return { count: state.count + 1 };
    case "decrement":
      return {
        count: state.count !== 0 ? state.count - 1 : (state.count = 0),
      };
    case "reset":
      return { count: (state.count = 0) };
    default:
      throw new Error("Error: Invalid");
  }
};

I also implemented the Error Boundary for the project in the case that any error is thrown in the background, the user will be notified of said error on the page.

  <ErrorBoundary FallbackComponent={ErrorFallback} onReset={() => {
        navigate('/')
      }} >
            <Routes>
                <Route path="/" element={<Counter />} />
                <Route path="/test-error" element={<TestError />} />
                <Route path="*" element={<ErrorPage />} />
            </Routes>
</ErrorBoundary>

The page looked like this:

I hosted the project on Vercel by adding new project and importing my code base from GitHub. The process of hosting was hitch-free and pretty easy.

The finished counter app can be found here.