Building a Counter app using React
Table of contents
- Here are the steps taken in creating a counter app using useState Hook and useReducer Hook;
- Build React App
- Make Counter Component File
- Build Counter with useReducer Hook
- Build Counter with useState Hook
- Register Component in App
- Implementing ErrorBoundary
- Implementing SEO
- React Helmet Basic Concepts and Usage
- Adding other features
- Run React Application
- Conclusion
Counter app is a simple tool for counting things and keeping track of numbers. It consist mainly of an increment button, decrement button, display, and reset button. This task was given by Altschool Africa for second semester examination.
This is a detailed breakdown on how to build a counter app from scratch. Focusing on the approach used in building the counter app using custom hooks, useState & useReducer, and also how the code was implemented while applying SEO, error Boundary and error 404 page.
First focus on building the counter app before applying other features. This means creating two counter app using useState and useReducer for each. The useState and useReducer are react hooks which will can be used to implement the counter app functionalities.
Here are the steps taken in creating a counter app using useState Hook and useReducer Hook;
Step 1: Build React App
Step 2: Make Counter Component File
Step 3: Build Counter with useState Hook
Step 4: Build Counter with useReducer Hook
Step 5: Style the components
Step 6: Implement Error Boundary and SEO
Step 7: Update App.js File
Step 8: Run React Application
Build React App
To create the react, type the following command on the terminal and then run the command.
npx create-react-app counter-app
Enter into the project’s root.
cd counter-app
Make Counter Component File
Create the /Counter.js (useReducer) file and /CounterTwo.js (useState) file.
Build Counter with useReducer Hook
Starting with /Counter.js (useReducer). Import react and create the export default function as follows;
import React from 'react'
function Counter() {
return (
<></>
);
}
export default Counter;
The useReducer counter app consists of 4 main parts; the display, the increment button, decrement button and reset button.
import React from 'react'
function Counter() {
return (
<>
<div clanpm run buldssName='counter display__flex_center flex-coloumn'>
<div className="increment__decrement__output display__flex align__center">
{/* Decrements the count */}
<button className='minus__btn'>-</button>
<p className='display__flex_center output'>Count</p>
{/* iNcrements the count */}
<button className='add__btn'>+</button>
</div>
<div className="input__reset display__flex align__center">
<Link to='/CounterTwo'>
<button>Input Value</button>
</Link>
<button>Reset</button>
</div>
</div>
</>
);
}
export default Counter;
Using CSS flex-box, style the component to achieve the layout I wanted. Flex-box was used to achieve a responsive layout for all screen sizes.
Inside the CounterTwo.js file, add the logic to create the counter feature using useReducer.
import React from 'react'
import { useReducer } from "react";
const reducer = (state, action)=>{
if(action.type==='decrement'){
return {counter:state.counter-1}
} else if (action.type==='increment') {
return {counter:state.counter+1}
} else if(action.type==='zero'){
return {counter:0}
}
else{
throw new Error()
}
}
const initialstate = {counter:0}
// Counter using useReducer
function Counter() {
const [state, dispatch]=useReducer(reducer, initialstate);
return (
<>
<div className='counter display__flex_center flex-coloumn'>
<div className="increment__decrement__output display__flex align__center">
{/* DEcrements the count */}
<button className='minus__btn' onClick={()=>dispatch({type:'decrement'})}>-</button>
<p className='display__flex_center output'>{state.counter}</p>
{/* iNcrements the count */}
<button className='add__btn' onClick={()=>dispatch({type:'increment'})}>+</button>
</div>
<div className="input__reset display__flex align__center">
<Link to='/CounterTwo'>
<button>Input Value</button>
</Link>
<button onClick={()=>dispatch({type:'zero'})}>Reset</button>
</div>
</div>
</>
);
}
export default Counter;
Build Counter with useState Hook
In the /CounterTwo.js (useState). Import react and created the export default function, same styling used for /Counter.js (useReducer) will be used for /CounterTwo.js, the only difference is an Input field will be added to the /CounterTwo.js file.
import React from 'react'
function CounterTwo() {
// Conter using useState hook
return (
<>
<div className='counter display__flex_center flex-coloumn'>
<div className="increment__decrement__output display__flex align__center">
{/* Decrement the count */}
<button className='minus__btn'>-</button>
<p className='display__flex_center output'>0</p>
{/* Increment the count */}
<button className='add__btn'>+</button>
</div>
<div className="input__reset display__flex align__center">
<input type="text" placeholder='Input Value' required />
<button>Reset</button>
</div>
</div>
</>
);
}
export default CounterTwo;
Proceed to add logic using useState Hook;
import React from 'react'
import { useState } from 'react';
function Counter() {
const [count, setCount] = useState(0);
const decrementValue = () => {setCount(count - 1) }
const incrementValue = () => { setCount(count + 1)}
const resetValue = () => { setCount(0) }
const ChangeCounterValue = (value) => { setCount(parseInt(value))}
// Conter using useState hook
return (
<>
<div className='counter display__flex_center flex-coloumn'>
<div className="increment__decrement__output display__flex align__center">
{/* Decrement the count */}
<button className='minus__btn' onClick={decrementValue}>-</button>
<p className='display__flex_center output'>{count}</p>
{/* Increment the count */}
<button className='add__btn' onClick={incrementValue}>+</button>
</div>
<div className="input__reset display__flex align__center">
<input type="text" placeholder='Input Value' required onKeyUp={(event) =>{
if(event.key === "Enter") {
ChangeCounterValue(event.target.value)
}
}} />
<button onClick={resetValue}>Reset</button>
</div>
<Link to='/Counter'>
<button className='btn__back'>Back</button>
</Link>
</div>
</>
);
}
export default CounterTwo;
Register Component in App
The App.js file is the main component of React, therefore the Counter component and CounterTwo component will be registered in the App component.
import React from 'react'
import Counter from './Counter';
import CounterTwo from './CounterTwo';
function App() {
return (
<div>Main App</div>
);
}
export default App;
Next in the /app.js file, import router, routes and link and used them to link the different pages in the app. Import /app.css file for the styling. The /app.css will be imported in each component too.
import React from 'react';
import './App.css';
import Counter from './Counter';
import CounterTwo from './CounterTwo';
import {
BrowserRouter as Router,
Route,
Routes,
Link } from 'react-router-dom';
function App() {
return (
<Router>
<div className="App display__flex_center flex-coloumn">
<div className="over-lay display__flex align__center flex-coloumn">
<nav>
<Link to='/' className='color__white'>
<h1 className="font__medium color__white"><span>George</span>Joseph</h1>
</Link>
<ul className='color__white font'>
<Link to='/' className='color__white'>
<li>Home</li>
</Link>
<Link to='/About' className='color__white'>
<li>About Me</li>
</Link>
<Link to='/Error' className='color__white'>
<li>Error</li>
</Link>
</ul>
<Link to='Counter'>
<button>Counter</button>
</Link>
</nav>
<Routes>
<Route exact path="/" element={<Header />}/>
<Route exact path="/Counter" element={<Counter />}/>
<Route exact path='/CounterTwo' element={<CounterTwo />}/>
</Routes>
</div>
<img src="./images/bg.gif" className="bg-gif" alt="" />
</div>
</Router>
);
}
export default App;
Implementing ErrorBoundary
ErrorBoundary is like a strategy for handling errors in React components. First create an /ErrorBoundary.js file. Then use a class based component to write the following code;
import { Component } from 'react';
class ErrorBoundary extends Component {
constructor(props) {
super(props)
this.state = {
hasError: false
}
}
static getDerivedStateFromError(error){
return{
hasError: true
}
}
render() {
if (this.state.hasError) {
return <div className='error__boundary font__poppin color__white'>
<p>OOPS !</p>
<h4>404 PAGE CANNOT BE FOUND</h4>
</div>
}
return this.props.children
}
}
export default ErrorBoundary;
CSS flex-box as well as positioning was used to design the error 404 page that'll be returned on the screen if an error were to be encountered.
Then wrapped each of the individual file components including the /app.js file in an ErrorBoundary tag. This is how the /app.js looked like after implementation;
<ErrorBoundary>
<Router>
<Helmet>
<title>Counter App created using React Js.</title>
<meta name='description' content="Counter App implmented using Hooks, useReducer for decrement and increment of count. Contains an error page and about me; George Joseph Emmanuel" />
<link rel="canonical" href="/App" />
</Helmet>
<div className="App display__flex_center flex-coloumn">
<div className="over-lay display__flex align__center flex-coloumn">
<nav>
<Link to='/' className='color__white'>
<h1 className="font__medium color__white"><span>George</span>Joseph</h1>
</Link>
<ul className='color__white font'>
<Link to='/' className='color__white'>
<li>Home</li>
</Link>
<Link to='/About' className='color__white'>
<li>About Me</li>
</Link>
<Link to='/Error' className='color__white'>
<li>Error</li>
</Link>
</ul>
<Link to='Counter'>
<button>Counter</button>
</Link>
</nav>
<Routes>
<Route exact path="/" element={<Header />}/>
<Route exact path="/About" element={<About />}/>
<Route exact path="/Counter" element={<Counter />}/>
<Route exact path='/CounterTwo' element={<CounterTwo />}/>
<Route exact path="/Error" element={<Error />}/>
</Routes>
</div>
<img src="./images/bg.gif" className="bg-gif" alt="" />
</div>
</Router>
</ErrorBoundary>
Implementing SEO
With SEO, you’re optimizing your website so it organically shows up on the first page of the search result. In the project's directory in the terminal, Istall react-helmet-async.
Once the installation completes, move on to importing and utilizing the Helmet component library.
React Helmet Basic Concepts and Usage
Imported these two components from react-helmet-async called Helmet and HelmetProvider in each of the component file.
- HelmetProvider will wrap the entire app component in order to create context and prevent memory leaks. Therefore, this component will only need to be imported in the root App component.
import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
import App from './App';
import {HelmetProvider} from 'react-helmet-async'
import reportWebVitals from './reportWebVitals';
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
<React.StrictMode>
<HelmetProvider>
<App />
</HelmetProvider>
</React.StrictMode>
);
reportWebVitals();
- Helmet will be imported into any page component where you want to implement meta tags. Think of <Helmet> as the <head> tag for the page in question.
import {Helmet} from 'react-helmet-async';
Wrap each component in a helmet provider tag as follows;
<Helmet>
<title>Counter App created using React Js.</title>
<meta name='description' content="" />
<link rel="canonical" href="/App" />
</Helmet>
The Helmet component is used to add the title and description metadata to each of the pages.
Adding other features
This is where the Navigation bar, /header.js file and /errorPage.js file were created and also styled using flex-box to achieve a responsive web page. Comments should be implemented to make the code more readable.
Run React Application
To check the results of the code, run the command in the app terminal; npm start
cd counter-app
npm start
Here's the link to the final outcome of the counter app; https://modern-counter.netlify.app/
Conclusion
This quick guide covers how to build a simple React counter app using custom hooks.
To build such functionality, various concepts of React were covered; starting with setting up a new react app and created a simple counter component using the useReducer hook and the useState hook.
This article also showed how to invoke deep state in React, how to manage state via useReducer and useState most importantly, how to use if-else statement in React to create the counter app. Also how to implement SEO and Error Boundary were addressed.
Subscribe to my newsletter
Read articles from Joe George directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by
Joe George
Joe George
Meet Joseph George, a multi talented web developer and Computer Engineering graduate, who has a passion for creating dynamic and engaging digital experiences. With a keen eye for detail and a wealth of technical knowledge, Joseph has built a reputation for delivering high-quality projects that exceed expectations. Whether it's designing beautiful websites or developing complex applications, Joseph is always up for a challenge and eager to push the boundaries of what's possible in the digital realm.