DEV Community

Cover image for React Tutorial #1: Simple counter using Hooks
_CODE
_CODE

Posted on • Updated on

React Tutorial #1: Simple counter using Hooks

Hi everybody!

Today, we're going to learn how to create a very basic counter in React using hooks.

Let's assume that, at this point, we all already know the concept of component in React and the fact that every component has something called state that we need to manage in order for our app to work the way we want.

In this post, we'll be covering useState and useReducer hooks. We're going to study two different examples (each of them using a different hook) that will lead to the same result.

With all that said, let's get it started!

Main idea

The main idea is to create a basic counter that is going to be able to perform the following actions:

  1. Adding / subtracting one single unit to / from the total count.
  2. Adding / subtracting an arbitrary quantity to / from the total count.

Structure and styles

We'll be using the same HTML structure and CSS styles for both examples. The structure will basically consist of a total count, some buttons to call a few actions to be performed and an input field to introduce the quantity we want to add or subtract. Rest is just styling to make our counter's interface more user-friendly. But don't you rush. You could play with the code later.

Now, let's take a look at our counter's functionality.

Example 1: Using useState

Importing useState

In order to be able to use React hooks, we need to import them. So let's do it.

import React, {useState} from 'react';
Enter fullscreen mode Exit fullscreen mode

Setting up the state

First thing that comes into mind when it comes to a counter, is, obviously, the total count. So we need to define it as part of the state and initialize its value to zero.

const [count, setCount] = useState(0);
Enter fullscreen mode Exit fullscreen mode

useState returns a stateful value and a function to update it.

Same happens if we're planning to add / subtract an arbitrary quantity to / from the total count. Our app's state should know about that quantity. So, let's make it part of the state as well. Its default value will also be initialized to zero.

const [quantity, setQuantity] = useState(0);
Enter fullscreen mode Exit fullscreen mode

Adding functionality

Now that we have defined the state for our app, we can start adding some basic functionality to the counter.

1. Adding / subtracting one single unit

First thing to remark is that we'll be triggering functionality through buttons, which means that these should reference functions to handle the actions that will be performed. We'll use React's onClick event handler for such purpose.

<button onClick={handleSubtractOne}>-1</button>
<button onClick={handleAddOne}>+1</button>
Enter fullscreen mode Exit fullscreen mode
const handleSubtractOne = () => {
   setCount(count - 1);
}
Enter fullscreen mode Exit fullscreen mode
const handleAddOne = () => {
   setCount(count + 1);
}
Enter fullscreen mode Exit fullscreen mode

And that would be it for this basic functionality. Easy, right? Now, let's take a further step.

2. Adding / subtracting an arbitrary quantity

For implementing this functionality, we'll need an input field to enter the desired quantity and a couple of buttons as well.

<input type="text" value={quantity} onChange={handleOnChange} />
Enter fullscreen mode Exit fullscreen mode
<button onClick={handleSubtractQuantity}>-</button>
<button onClick={handleAddQuantity}>+</button>
Enter fullscreen mode Exit fullscreen mode

The onClick event handlers work in the exact same way as the others, with the only distinction that these ones call different handlers (because the functionality they're triggering is different).

The onChange event handler declared on the input element is used to store the entered value into the state.

Also note that the value we are passing to the value attribute on the input is the one stored in the state, which will be changing accordingly.

const handleOnChange = (e) => {
   setQuantity(e.target.value);
}
Enter fullscreen mode Exit fullscreen mode
const handleSubtractQuantity = () => {
   if(quantity)
      setCount(count - parseInt(quantity, 10));
}
Enter fullscreen mode Exit fullscreen mode
const handleAddQuantity = () =>{
   if(quantity)
      setCount(count + parseInt(quantity, 10));
}
Enter fullscreen mode Exit fullscreen mode

Important: Since input fields in HTML can't retrieve its value as a number (not even when the input type is set to number, it always takes a string as a value), it's necessary to parse it to integer before using it. Otherwise, it will be concatenated to the current count.

Also note that we're adding a condition to make sure no empty value is added or subtracted, which would result in a NaN.

3. Reseting the counter

Since we want our counter to be the most functional possible, we're going to add a button to reset the counter to zero. Its event handler will reference a function that will just set count and quantity state values to zero.

<button onClick={handleResetCounter}>Reset counter</button>
Enter fullscreen mode Exit fullscreen mode
const handleResetCounter = () => {
   setCount(0);
   setQuantity(0);
}
Enter fullscreen mode Exit fullscreen mode

And that's it! Our counter is now ready to use.

Check the full implementation here:
https://codesandbox.io/s/beautiful-mahavira-r1idm

Example 2: Using useReducer

Now, we're going to create the exact same counter but, this time, its functionality will be implemented using React's useReducer hook.

Importing useReducer

As we did in the other example, we need to import the hook we're going to work with.

import React, {useReducer} from 'react';
Enter fullscreen mode Exit fullscreen mode

Setting up the state

For this new case, we're going to set up the state in a slightly different way: we'll specify an initial state and also a reducer function that will take care of all the functionality, as it's required by useReducer. Both the initial state and the function will be taken as parameters by this hook.

const initialState = {count: 0, quantity: 0};
Enter fullscreen mode Exit fullscreen mode
const [state, dispatch] = useReducer(reducer, initialState);
Enter fullscreen mode Exit fullscreen mode

useReducer returns the current state and a dispatch method.

The reducer function

The approach of this hook is to have a reducer function that accepts the state of the app (or component) and an action as parameters, and, based on that action, the state is managed one way or another.

So let's take a look at the reducer function that we'll be using:

const reducer = (state, action) => {
  switch (action.type) {
    case "addOne":
      return {...state, count: state.count + 1};
    case "subtractOne":
      return {...state, count: state.count - 1};
    case "addQuantity":
      return {...state, count: state.count + parseInt(state.quantity, 10)};
    case "subtractQuantity":
      return {...state, count: state.count - parseInt(state.quantity, 10)};
    case "setQuantity":
      return {...state, quantity: action.payload};
    case "resetCounter":
      return initialState;
    default:
      throw new Error();
  }
};
Enter fullscreen mode Exit fullscreen mode

This function contains all the possible use cases that the counter is able to perform. If the action type passed in doesn't appear in the list of specified functions, an error will be thrown.

Important: Don't forget to spread the state every time you set any of its fields to keep the rest of the values as they are. Otherwise, your stored data will be overwritten with the returned value.

The handlers

We'll be using the same handlers, but now, they won't update the state values directly using a setter function. Instead, they will dispatch different actions that will return values to useReducer, which will handle them properly to update the state.

useReducer throws actions in the way Redux does, so, if you're familiarized with Redux, you'll find its behavior pretty much the same.

const handleSubtractOne = () => {
   dispatch({type: "subtractOne"});
};
Enter fullscreen mode Exit fullscreen mode
const handleAddOne = () => {
   dispatch({type: "addOne"});
};
Enter fullscreen mode Exit fullscreen mode
const handleSubtractQuantity = () => {
   if (state.quantity)
      dispatch({type: "subtractQuantity"});
};
Enter fullscreen mode Exit fullscreen mode
const handleAddQuantity = () => {
   if (state.quantity)
      dispatch({type: "addQuantity"});
};
Enter fullscreen mode Exit fullscreen mode
const handleResetCounter = () => {
   dispatch({type: "resetCounter"});
};
Enter fullscreen mode Exit fullscreen mode
const handleOnChange = (e) => {
   dispatch({type: "setQuantity", payload: e.target.value});
};
Enter fullscreen mode Exit fullscreen mode

And that's all.

Check the full implementation here:
https://codesandbox.io/s/quizzical-framework-3r2pp

I hope you find this tutorial useful and see you in the next.


🎉 Follow me on Instagram and Twitter for more related content: @underscorecode

Discussion (0)