Photo by Artem Sapegin on Unsplash
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.