A custom counter article with some amazing features, written in React .

Starting from the WelcomeCounterApp component i.e., our Home page, which is in the Welcome.js file. The output and the code will be shown below:

import React from "react";

import {Link} from "react-router-dom";

import "./App.css"

import { Helmet } from 'react-helmet-async';

const WelcomeCounterApp = () =>{

return(

<div>

<Helmet>

<title>Home</title>

<meta name="description" content="home counter" />

<link rel="canonical" href="/" />

</Helmet>

<div className="link-container">

<Link className="link" to="/custom-counter">Custom counter</Link>

<Link className="link" to="/useReducer-counter">useReducer counter</Link>

<Link className="link" to="/CustomReducer-counter">CustomReducer counter</Link>

<Link className="link" to="/Error">Click me</Link>

</div>

<h1>Welcome to my Counter App</h1>

<p>My Counter App is an application with three features,

which helps us change our value either by increasing, decreasing

or resetting the value using the available buttons provided.

The links we have are :

</p>

<li>Go to Home</li>

<li>useReducer counter</li>

<li>CustomReducer-Hook counter</li>

<li>custom counter</li>

<li>Click me link is to test error boundary</li>

</div>

)

}

export default WelcomeCounterApp;

In this project, we imported some helping tools like BrowserRouter, which is used for navigating from one component to another. The other helping tool is the HelmetProvider will wrap the App component in order to create context and prevent memory leaks. Therefore, both the HelmetProvider and BrowserRouter will need to be imported into the index.js component, which our App will be wrapped in.

The illustration is shown below:

import React, { StrictMode } from 'react';

import { createRoot } from 'react-dom/client';

import {BrowserRouter} from "react-router-dom";

import {HelmetProvider} from "react-helmet-async";

import App from './App';

const rootElement = document.getElementById('root');

const root = createRoot(rootElement);

root.render(

<BrowserRouter>

<StrictMode>

<HelmetProvider>

<App />

</HelmetProvider>

</StrictMode>

</BrowserRouter>

);

The HelmetProvider is imported from “react-helmet-async”. The two components we'll be importing from react-helmet-async are called Helmet and HelmetProvider. The HelmetProvider is used in the index.js file which wraps our App component. The Helmet will be seen subsequently, later in this article and we will see how it is being used in some components. What the Helmet component allows us to do is to set header properties, e.g, title, meta tag, etc. The Helmet will be explained better at the end of this article.

Going further to our root component, i.e, App.js, we imported some components which we have already created, you can find them below and see how they are being used in the same component.

import React from "react";

import Custom from "./Custom";

import Reduce from "./Reduce";

import CustomReducer from "./CustomReducer";

import WelcomeCounterApp from "./Welcome";

import ErrorBoundary from "./ErrorBoundary";

import Error from "./Error";

import NotExitingPage from "./NotExitingPage";

import {Routes, Route} from "react-router-dom";

function App(){

return(

<div>

<ErrorBoundary>

<Routes>

<Route path="/" element={<WelcomeCounterApp />} />

<Route path="/CustomReducer-counter" element={<CustomReducer />} />

<Route path="/custom-counter" element={<Custom />} />

<Route path="/useReducer-counter" element={<Reduce/>} />

<Route path="/Error" element={<Error pic="Good" title="Better" />} />

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

</Routes>

</ErrorBoundary>

</div>

)

}

export default App;

The Error boundary is a React component that catches JavaScript errors anywhere in their child component tree, logs those errors, and displays a fallback UI. Error boundary is always used to wrap all of our components in the App component. So, anywhere we have an error in our code, the Error boundary catches the component and displays an error like “Something went wrong” instead of crashing the entire app.

The ErrorBoundary component in ErrorBoundary.js and its code including the output will be shown:

The ErrorBoundary code we used is obtained from the official react website. Though there is one we imported we didn't make use of it here.

import React from "react";

class ErrorBoundary extends React.Component {

constructor(props) {

super(props);

this.state = { hasError: false };

}

static getDerivedStateFromError(error) {

// Update state so the next render will show the fallback UI.

return { hasError: true };

}

componentDidCatch(error, errorInfo) {

// You can also log the error to an error reporting service

// logErrorToMyService(error, errorInfo);

}

render() {

if (this.state.hasError) {

// You can render any custom fallback UI

return <h1>Something went wrong.</h1>;

}

return this.props.children;

}

}

export default ErrorBoundary;

The “Click me” is a link to another component, if the component had a javascript error when clicked on, it then brings out “Something went wrong” which you can see below.

Still, on our App.js, the Route with s is what we use to wrap all our app components while the one without s is the individual Route with path name to each of the components. The one with “*” means any path name that is not included in the Route, i.e, any path given will show us a 404 page in respect to {<NotExitingPage />}, ie, NotExitingPage component.

The 404 page means a web page that does not exist.

We created a folder called hooks, where we have our hooks.

One of the hooks we created is called UseCustom i.e, a custom hook we created ourselves. The UseCustom hook we created is shown below:

import {useState} from "react";

function UseCustom(initialValue = 0, digit){

const [value, setValue] = useState(initialValue);

function IncrementHandle(event){

event.preventDefault();

setValue(prev => prev + digit)

}

function OnChangeHandle(event){

event.preventDefault();

let num = Number(event.target.value)

setValue(num)

console.log(event.target.value)

}

function DecrementHandle(event){

event.preventDefault();

setValue(prev => prev - digit)

}

function ResetHandle(event){

event.preventDefault();

setValue(0)

}

return [value, OnChangeHandle, IncrementHandle, DecrementHandle, ResetHandle]

}

export default UseCustom;

The value in the Usecustom hook which is the state is 0 by default.

Instead of hard coding the logic in the Custom hook, we did everything in the UseCustom hook and then exported the logic and the value in the Custom component. It is shown below:

import React from "react";

import {Link} from "react-router-dom"

import UseCustom from "./hooks/UseCustom.js"

import "./App.css"

import {Helmet} from "react-helmet-async";

const Custom = () =>{

const [value, OnChangeHandle, IncrementHandle, DecrementHandle, ResetHandle] = UseCustom(0, 12)

return(

<div>

<Helmet>

<title>My Custom-Counter</title>

<meta name="description" content="Custom-Counter"/>

<link rel="canonical" href="/custom-counter" />

</Helmet>

<div className="link-container">

<Link className="link" to="/">Go to Home</Link>

<Link className="link" to="/useReducer-counter">useReducer counter</Link>

<Link className="link" to="/CustomReducer-counter">CustomReducer counter</Link>

<Link className="link" to="/Error">Click me</Link>

</div>

<h1>custom-Hook-counter: {value}</h1>

<h1 className="count">{value}</h1>

<input onChange={OnChangeHandle} type="number" placeholder="number" />

<div className="button-container">

<button onClick={IncrementHandle}>Increment</button>

<button onClick={DecrementHandle}>Decrement</button>

<button onClick={ResetHandle}>Reset</button>

</div>

</div>

)

}

export default Custom;

The onChangeHandle function helps us get the value being put in the input element. When a value is put, it is a string(s) by default, so, we then put it in a Number() method which then converts a string(s) into a number(s)

The 0 is what we put in our state and the digit is just another parameter that we made to be 12 in the code above.

The IncrementHandle function helps to increase the state or whatever state we set the input by 12.

The DecrementHandle function helps to decrease the state or whatever state we set the input by 12.

The ResetHandle function helps us to reset our value back to the initial state or value, i.e default state.

The three functions are then put in their respective buttons to perform their functions whenever any of the buttons is clicked.

As we mentioned at the beginning of this article above, on getting to see how a Helmet is used, that is it in the above code i.e, Custom component.

The Link used is a result of the BrowserRouter being installed and then we imported the Link from react-router-dom for easy navigation to other components.

The look of the Custom component in the browser.

Going further into another feature in our counter app, which is useReducer counter.

The work of a useReducer hook helps us to manage states and re-render component whenever the component changes the idea behind useReducer is to get you a more concrete way to handle complex states, so it is going to give you set action you can perform on your state and it is going to convert your current state to a new a version of the state base on the action you set it.

Before going into where we used our useReducer hook, we, first of all, created a Reducer function which we named CustomReducer in the Reducer.js file which is in the hooks folder, you will see it in the picture below:

function setValue(value) {

console.log(typeof value);

return Number(value);

}

export default function CustomReducer(initialState = 0, action) {

if (action === "Increment") return initialState + 1;

else if (action.type === "name") {

return setValue(action.payload);

} else if (action === "Decrement") return initialState - 1;

else if (action === "Reset") return 0;

else return initialState;

}

The above code is what we have in the Reducer.js i.e, it is our Reducer.

The setValue takes in a value as a parameter, which happens to be a string, so we then convert it using a Number() method. The setValue works in respect of the setValue(action.payload)because, the payload is additional information, ie, we used the input to set our value with the help of the payload and by nature, it is a string, so we used that to convert the string to a number.

The CustomReducer is a reducer function that takes in an initialvalue and an action. The switch statement you see in the reducer function that we created can also be replaced by the switch statement. So in this case, we used the if/else statement, the action is simply an act to be carried out, if the action is increment i.e, increment button, it then increases the count by 1 and it was the decrement button, the value in the dispatch function(“decrement”) will then act upon the reducer function in the if/else statement and carry out the condition set in the if/else statement, this also applies to the reset button.

The dispatch function in the Reduce component(Reduce.js) accepts an object but in this case, we did not use the “type” in the action we want to execute when it is called to update the state.

To get a better understanding of the “type”, actually, the “type” was just used once in the if/else statement in the reducer function and is also used in the Reducer component(Reducer.js). The work of the “type” is to send the type of action which is “name” as the name of the action in the Reduce component(Reduce .js) to the Reducer function to perform its job, ie, checking if the condition is meant in the Reducer function, to execute the code in it which, of course, is updating the state.

The action to be executed is specified in our reducer function, which in turn, is passed to the useReducer. The Reducer function will then return the updated state.

Normally, the action performed by the user. It can be a value of any type. By convention, an action is usually an object with a type property identifying it and, optionally, other properties with additional information.

In our Reduce component(Reduce .js) we only talked about 3 handlers and how they work, the 4th one is called onChangehandler which is put in our input element. The function for the handler, has a dispatch, which we now know what it does. It takes in an action type which is “name”.The “type” must match one of the switch cases or if/else statements in your Reducer function.

The value of payload is the value you want to update the state with, ie, we are using the input to set our state. When you look at our Reducer component, you will see we made an action type which is “name” because the type is matching the conditional statement in the Reducer function, so it will then return setValue, ie, we now use payload value to update with the help of our setValue. You can check your console in the developer tool as you use the input to set your value, it is the work of the payload with the help of the setValue.

import React, {useReducer} from "react";

import CustomReducer from "./hooks/Reducer"

import "./App.css"

import {Link} from "react-router-dom"

import { Helmet } from 'react-helmet-async';

export default function Reduce() {

const [count, dispatch] = useReducer(CustomReducer, 0)

function OnchangeHandle(event){

dispatch({type: "name", payload: event.target.value})

event.preventDefault();

console.log(event.target.value)

}

return (

<div>

<Helmet>

<title>useReducer-Counter-Hook</title>

<meta name="description" content="Reducer counter" />

<link rel="canonical" href="/useReducer-counter" />

</Helmet>

<div className="link-container">

<Link className="link" to="/">Go to Home</Link>

<Link className="link" to="/custom-counter">Custom counter</Link>

<Link className="link" to="/CustomReducer-counter">CustomReducer counter</Link>

<Link className="link" to="/Error">Click me</Link>

</div>

<h1>useReducer-hook-counter:</h1>

<h1 className="count">{count}</h1>

<input onChange={OnchangeHandle} type="number" placeholder="number" />

<div className="button-container">

<button onClick={()=>dispatch("Increment")}>Increment</button>

<button onClick={()=>dispatch("Decrement")}>Decrement</button>

<button onClick={()=>dispatch("Reset")}>Reset</button>

</div>

</div>

);

}

The code above is what we have in the Reduce component(Reduce.js)

The Reducer function is imported in the Reduce component in Reducer.js. We also imported our useReducer hook from react, since we are using it, we passed in the Reducer function we created in the useReducer hook and also with a state as a second parameter, the useReducer returns an array which is a state and dispatch. The work of the dispatch function is to let you update the state to a different value and trigger a re-render.

The last feature is the CustomReducer counter. React allows us to be able to create a hook ourselves which is said to be a custom hook. We created a custom hook, which has a Reducer in it, the hook is called usecustomReducer, it can be found in the hook folder. The code to the custom hook is shown below:

import {useReducer} from "react";

export default function useCustomReducer(num){

console.log(num)

function setValue(value){

console.log(typeof value)

return Number(value)

}

function Reducer(initialState = 0, action){

if(action === "Increment") return initialState + 1;

else if(action.type === "name") {

return setValue(action.payload)

}

else if(action === "Decrement") return initialState - 1

else if(action === "Reset") return 0;

else return initialState;

}

const [count, dispatch] = useReducer(Reducer, num)

function OnchangeHandle(event){

dispatch({type: "name", payload: event.target.value})

event.preventDefault();

console.log(event.target.value)

// setValue(count.event.target)

}

return{count, dispatch, OnchangeHandle}

}

In the hook, we created a Reducer function and also used the useReducer hook, which we have gone through before in the useReducer counter feature. When you read about the previous feature you will find this particular feature easier to get.

The useCustomReducer is returning an object, mind you in the previous custom hook counter, that is, the UseCustom hook returned an array but in this hook (useCustomReducer) we are returning an object. We returned the value/state, the dispatch function which is used to update the state and the onChangeHandler which is used to set the state.

N.B. If a hook returns an array([x]), then you can name the variables yourself. If a hook returns an object({x}), then you must use the same variable names returned by the hook itself.

We created a component called CustomReducer in the CustomReducer.js file, where we want to use or consume our custom hook(useCustomReducer hook). The picture and the code will be shown below:

import React from 'react';

import useCustomReducer from './hooks/useCustomReducer';

import "./App.css"

import { Link } from 'react-router-dom';

import { Helmet } from 'react-helmet-async';

export default function CustomReducer() {

const { count, dispatch, OnchangeHandle } = useCustomReducer(0);

return (

<div>

<Helmet>

<title>CustomReducer-Counter</title>

<meta name="description" content="CustomReducer-Counter App" />

<link rel="canonical" href="/CustomReducer-counter" />

</Helmet>

<div className="link-container">

<Link className="link" to="/">

Go to Home

</Link>

<Link className="link" to="/custom-counter">

Custom counter

</Link>

<Link className="link" to="/useReducer-counter">

useReducer counter

</Link>

<Link className="link" to="/Error">

Click me

</Link>

</div>

<h1>CustomReducer-Hook-counter:</h1>

<h1 className="count">{count}</h1>

<input onChange={OnchangeHandle} type="number" placeholder="setValue" />

<div className="button-container">

<button onClick={() => dispatch('Increment')}>Increment</button>

<button onClick={() => dispatch('Decrement')}>Decrement</button>

<button onClick={() => dispatch('Reset')}>Reset</button>

</div>

</div>

);

}

As you can see, we imported the hook because we are using the hook in the component. The hook returns the state, dispatch, onChangeHandler, which we then put in their respective places. If you follow the custom counter feature, ie, the custom hook we first talk about in this article, you will understand what the custom hook is returning and how they are being used.

The page of the CustomReducer is shown below:

As we have mentioned above that react-helmet-async will be explained below in this article, we have come to the part where we will talk about it.

SEO stands for Search Engines Optimization and it is essentially focused on optimization, so your app or site can rank high in Google.

However, that can usually be done in two ways. You can, for example, pay for google ads so there is the advertisement platform and you can pay money essentially to rank high.

There is also SEO which is more focused on organically trying to rank high in google and in essence you could do that completely for free. SEO is quite complicated, there are over 200 metrics that google is using to determine a score for your site.

The meta tags help us to control how pages will be shown in the search engine, eg, the title, the description etc can be set for the React app. So to help to achieve that, we use react-helmet-async, it is a library. When you open a react project, after installation, in the public folder, check the index.html file, you will see the title and the meta description are written in there and that’s how the components we create will show and that is not supposed to be so. Each component has to have its own meta description and title.

How to go about the SEO, we installed react-helmet-async, after installation, in our main.js we then imported the HelmetProvider and then wrapped the App component in it, like it is shown at the beginning of this article. Then in each of the components we created ourselves, we then imported the Helmet from react-helmet-async, you can see the Helmet and how it's being used in some components we created. What the Helmet allows us to do, is to set a specific set of header properties which you can see in the Helmet.

To get a better SEO indicator, in order to help you know where and where that needs to be fixed, you can find a chrome extension to show your SEO. It helps show if your title, description, canonical etc have been set. In the codes above in the components we created, you can see the Helmet has title and description. For the canonical, if not attended to, it indicates the canonical URL is missing, ie, the page is not protected against internally duplicate content. So to prevent that, we put in a link tag and in the link tag, the relation(rel) will be canonical and the “href” will be the route of your page. All that I have said, can be found in each component we created.

The next thing I will want us to talk about is Robots.txt. This is provided by default with create-react- app, you will see it in the public folder. It is shown below:

In our robots.txt file, we will be able to tell crawlers like the Google crawlers to not crawl our page, So currently, because Disallow is empty, as you can see above, google will crawl all our pages but we can also put a forward slash(/) and that will mean that we have disallowed(Disallow) all pages not to be crawled. If you want some part of our app not to be crawled, we then just put a forward slash(/) and the name of the component, all in front of the Disallow: ie, if you do not want some pages/components not to be crawled you the do this, for example, Disallow: /Login

Disallow: /User

Disallow: /Result

NOTE

It is good to know that not allowing Google to crawl your site does not guarantee you it will not show up in the search result. If you want to prevent that, you can use a meta tag with the “noindex” property. What you will just do is to include <meta name=”robots” content=”noindex” /> in the particular component helmet. But this is not what we want to achieve.

The next thing to do is to allow Google to crawl our sitemap.txt.

  1. Deploy your site first.

  2. When you are done deploying, you then create a sitemap.txt file(public), as you will see in the picture below:

  1. Go to the Google Search Console and add a meta tag that is given by Google. The steps to go about this:
  • When you go to the site i.e, Google Search Console, it will ask for the deployed site, you then pass the URL and click to verify.

  • After clicking on the verify, it will then display something, having two options in it, you can either download the HTML file or click on the HTML tag below and copy the tag to your HTML file in your public folder.

  • Make sure you redeploy your site because of the changes you already made and for it to update. What we did, as we pushed our code initially to Github and connected Netlify to it for hosting, so as we updated the code and pushed it to Github, the site got automatically updated and then we clicked verify on the google website.

  • Still, on the same Google site, click on sitemaps, it will load and then prompt you to put in the URL sitemap, the picture is shown below:

  • You then will see a link to your hosted site and in front of it, you then type in “sitemap.txt”, ie, in the space provided as you see above, and then click on “submit”.

  • Your status then shows you “success”.