DEV Community

Cover image for Custom React Hooks: useCounter
Ludal 🚀
Ludal 🚀

Posted on • Updated on • Originally published at blog.iamludal.fr

Custom React Hooks: useCounter

In the last article of the Custom React Hooks series, we've implemented the useBoolean hook. Today, we'll create a not more complicated one: useCounter. As its name suggests, this hook will help us manage a counter state thanks to utility functions, such as increment and decrement. Ready? Let's jump right into it! 🚀

Motivation

As always, let's see why would you want to implement this hook. Imagine you are building an e-commerce website. Whenever a customer wants to buy an item, he should be able to select the quantity he wants. This is how the user interface might look like (let's forget the style):

UI

And the source code of the Cart component might look like this:

const Cart = () => {
  const [quantity, setQuantity] = useState(0);

  return (
    <div className="Cart">
      <h1>My Cart</h1>
      <Item
        label="My awesome item"
        quantity={quantity}
        onIncrement={() => setQuantity((q) => q + 1)}
        onDecrement={() => setQuantity((q) => q - 1)}
        onReset={() => setQuantity(0)}
      />
    </div>
  );
}
Enter fullscreen mode Exit fullscreen mode

ℹī¸ Of course, all the examples and code snippets will be extremely simplified in order to only focus on what matters the most in this article.

The above component could be simplified by introducing a custom useCounter hook:

function Cart() {
  const quantity = useCounter(0);

  return (
    <div className="Cart">
      <h1>My Cart</h1>
      <Item
        label="My awesome item"
        quantity={quantity.value}
        onIncrement={quantity.increment}
        onDecrement={quantity.decrement}
        onReset={quantity.reset}
      />
    </div>
  );
}
Enter fullscreen mode Exit fullscreen mode

Sounds interesting? Let's implement this hook! 😎

Implementation

Our useCounter hook will have a state to hold the counter value. It will also have 3 methods to update its value: increment, decrement, and reset. With this in mind, we can create the hook in the following way:

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

  const increment = () => setValue(c => c + 1);
  const decrement = () => setValue(c => c - 1);
  const reset = () => setValue(initialValue);

  return { value, increment, decrement, reset };
};
Enter fullscreen mode Exit fullscreen mode

Now, some of you might wonder why the 3 exported methods are not wrapped into the useCallback hook (which prevents creating new functions at every render, more info here). In our case, the component is so simple that calling useCallback 3 times to improve performances could have the opposite effect. This hook should be used when you know your function will be used in a more complex component, for instance one that holds a big list of items, each one using your function as an onClick event.

ℹī¸ For more information, don't hesitate to have a look at this article from Dmitri Pavlutin, which goes into details about why, when and how to use the useCallback hook. The same applies for useMemo and useRef (except when used as a reference to an element).

That being said, our new custom hook is now ready to use! đŸĨŗ

Improvement ideas

To go further, here are some ideas for improvement to enhance the useCounter hooks. Don't hesitate to try implementing one or more of this ideas, so that you can practice on your own.

  • Adding an increment/decrement step (counter.increment(step))
  • Adding a min/max value (useCounter({ min: 0, max: 10, initial: 0}))
  • Manually set the counter value (counter.set(value))

Conclusion

So far, the custom hooks we've been creating are very simple. In the next articles, we'll start implementing more complex hooks to really help us simplify our components code and avoid code duplication. That being said, thanks for reading me. I hope you enjoyed this article. If you did, don't hesitate to have a look at my other ones. Also, feel free to write some comments if you have any questions or remarks. 🤗


Source Code available on CodeSandbox


Support Me

If you wish to support me, you can buy me a coffee with the following link (I will then probably turn that coffee into a new custom hook... ☕)

Buy Me A Coffee

Discussion (0)