DEV Community

Cover image for React: Mastering custom Hooks and optimizing logic reusability
Marcos Vinicios da Silva Neves
Marcos Vinicios da Silva Neves

Posted on

React: Mastering custom Hooks and optimizing logic reusability

Hey there! Yes, I'm talking to you! The one reading this article and wondering: "What on Earth are Custom Hooks, and why should I care about them?". Well, strap in, because I'm about to take you on a journey that goes beyond the traditional React Hooks. Ready to dive deep and uncover the magic behind Custom Hooks, and better yet, when to use these bad boys as opposed to our beloved pure functions?

Many developers find themselves at a crossroads, trying to decide between implementing a Custom Hook or simply crafting a pure function. And, to be honest, this decision can seem baffling at first glance. But fear not, I'm here to guide the way! In this article, we'll not only unravel the enigmatic Custom Hooks, but we'll also delve into their states and, most crucially, the ideal scenarios to choose between them and pure functions. So, if you're looking to optimize and elevate your React code to a whole new level, you've come to the right place. Shall we?

Talk is cheap, show me the code!

Someone happy and surprised by getting a new error message while debugging a code

Let's kick things off with a hands-on example, one you've likely encountered or will eventually face in your developer journey. Picture a component that needs to notify a user whether they're connected to the internet or not. Sounds simple, right? Let's dive into the code:

import React, { useState, useEffect } from 'react';

function ConnectionChecker() {
    const [isOnline, setIsOnline] = useState(navigator.onLine);

    useEffect(() => {
        const updateStatus = () => {
            setIsOnline(navigator.onLine);
        };

        window.addEventListener('online', updateStatus);
        window.addEventListener('offline', updateStatus);

        return () => {
            window.removeEventListener('online', updateStatus);
            window.removeEventListener('offline', updateStatus);
        };
    }, []);

    return (
        <div>
            {isOnline ? <p>You're online! 🌐</p> : <p>Oops! You're offline... πŸ˜”</p>}
        </div>
    );
}

export default ConnectionChecker;
Enter fullscreen mode Exit fullscreen mode

This component, aided by useEffect, checks if the browser is online or offline, and based on that information, displays a message to the user. It works wonderfully!

Now, envision wanting to integrate this very logic into multiple other components within your app. Are you going to copy and paste? Absolutely not! This brings us to a pivotal question: How can we reuse this logic?

Refactoring for Reusability

A running cat

At first glance, one might assume the most straightforward solution would be to extract the logic into a pure function. That's usually a great instinct when refactoring: identify repeated or extractable logic, put it into its separate function, and call that function where needed. But here's the catch when it comes to React: the Rules of Hooks.

The Rules of Hooks

Before we proceed, let's briefly revisit these rules:

  1. Only Call Hooks at the Top Level: You can't call Hooks inside loops, conditions, or nested functions. They should always be used at the top level of your React functions. This ensures hooks are called in the same order each time a component renders, preserving the state between multiple useState and useEffect calls.

  2. Only Call Hooks from React Functions: Hooks can only be called inside the body of a function component or a custom hook.

Now, why do these rules exist? They're not just there to be a hurdle. They ensure that your components maintain a predictable order of execution, making your components more readable, maintainable, and less prone to unexpected bugs.

Given these rules, if we attempt to extract the logic in a pure function, we'd hit a wall. We're dealing with a useEffect and useState inside our logic – both of which are hooks and thus must adhere to the Rules of Hooks.

Creating our Custom Hook

Captain hook

Instead of a pure function, this is a perfect situation to create a custom hook. Let's dive in:

import { useState, useEffect } from 'react';

function useConnectionStatus() {
    const [isOnline, setIsOnline] = useState(navigator.onLine);

    useEffect(() => {
        const updateStatus = () => {
            setIsOnline(navigator.onLine);
        };

        window.addEventListener('online', updateStatus);
        window.addEventListener('offline', updateStatus);

        return () => {
            window.removeEventListener('online', updateStatus);
            window.removeEventListener('offline', updateStatus);
        };
    }, []);

    return isOnline;
}
Enter fullscreen mode Exit fullscreen mode

Notice how we've extracted the logic that checks the online status into a hook named useConnectionStatus. Now, let's refactor our original component to utilize this new custom hook:

import React from 'react';
import useConnectionStatus from './path-to-your-hook/useConnectionStatus';

function ConnectionChecker() {
    const isOnline = useConnectionStatus();

    return (
        <div>
            {isOnline ? <p>You're online! 🌐</p> : <p>Oops! You're offline... πŸ˜”</p>}
        </div>
    );
}

export default ConnectionChecker;
Enter fullscreen mode Exit fullscreen mode

With that refactor, not only have we made our component cleaner, but we've also created a piece of reusable logic that can be utilized across numerous components or even other projects. This, my friend, is the power and elegance of custom hooks!

Leveling Up: Advanced Custom Hooks

Goku leveling up

So, you're eager to dive deeper, huh? Alright, strap in! Once we master the basics of custom hooks, we often find ourselves in trickier waters. Sometimes, this complexity demands that our hook returns a function, ensuring the component using it has more control or dynamism. Let's unravel this with a hands-on example.

The Context

Imagine you're crafting a modal component. This modal needs to show or hide based on user actions, like a button click. Moreover, perhaps we want to pass some specific content to the modal every time we display it.

The Custom Hook

Let's craft a hook named useModal. It'll manage the modal's visibility state and also allow setting the modal's content:

import { useState } from 'react';

function useModal() {
    const [isVisible, setIsVisible] = useState(false);
    const [content, setContent] = useState(null);

    const showModal = (newContent = null) => {
        setContent(newContent);
        setIsVisible(true);
    };

    const closeModal = () => {
        setContent(null);
        setIsVisible(false);
    };

    return {
        isVisible,
        content,
        showModal,
        closeModal
    };
}
Enter fullscreen mode Exit fullscreen mode

Notice the twist! Beyond managing the visibility state (isVisible) and the content (content), we're returning two functions (showModal and closeModal). This unlocks amazing flexibility. We can define the modal's content on-the-fly when we pop it up using showModal, or simply use it to showcase the modal with predefined content.

Using the Hook

Let's see how this hook fits into a component:

import React from 'react';
import useModal from './path-to-your-hook/useModal';

function MyComponent() {
    const { isVisible, content, showModal, closeModal } = useModal();

    return (
        <div>
            <button onClick={() => showModal('Hello, I'm dynamic content!')}>
                Display Modal
            </button>

            {isVisible && (
                <div className="modal">
                    <p>{content}</p>
                    <button onClick={closeModal}>Close</button>
                </div>
            )}
        </div>
    );
}
Enter fullscreen mode Exit fullscreen mode

What we just pulled off is nothing short of magical! With our useModal hook, we've transformed a typical logic (managing a modal's visibility and content) into something reusable, agile, and most importantly, compliant with the Rules of Hooks. This way, you retain direct control of your app's logic within the component but without sprinkling repetitive code everywhere.

This showcases that custom hooks are not just about encapsulation but also about empowering and granting flexibility to your components. Hence, you build cleaner, more organized, and efficient React apps!

Conclusion:

Feeling pumped up about the limitless opportunities custom hooks offer? I certainly hope so! Through this deep dive into the React cosmos, we've unearthed how to turn repetitive logics into reusable marvels without breaking the sacred rules of hooks. With this newfound knowledge, the sky is merely the beginning!

Now, before you jet off to experiment (and I'm pretty sure you're itching to!), swing by my dev.to profile and give me a follow! I'm continually unraveling the intricacies of the coding world and sharing all the gems right there. And if this article resonated with you, don't forget to hit that like button!

Here's a special request: Drop a comment below with topics you'd love for me to tackle, lingering questions, constructive feedback, or even some good ol' praise (come on, who doesn't appreciate that?). Your insights are the pure fuel driving my next articles.

Until next time, always remember: knowledge is a journey, and I'm here to guide the way!

Like and subscribe!

Top comments (0)