Building a Custom Hook in React: Executing Actions on Specific Window Resizes
Tags: webdev, javascript, react, frontend
Published: 2023-09-25 14:18:43

## Table of Contents
&nbsp;1. [Introduction](#1-introduction)
&nbsp;2. [Prerequisites](#2-prerequisites)
&nbsp;3. [Setting Up the Project](#3-setting-up-the-project)
&nbsp;4. [Building the Custom Hook Step by Step](#4-building-the-custom-hook-step-by-step)
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 4.1. [Creating the hook](#41-creating-the-hook)
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 4.2. [Handling the Resize Event](#42-handling-the-resize-event)
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 4.3. [Adding Debounce for Performance](#43-adding-debounce-for-performance)
&nbsp;5. [Using the Custom Hook](#5-using-the-custom-hook)
&nbsp;6. [Conclusion](#6-conclusion)

## 1. Introduction

In the world of frontend development with React, creating reusable and efficient components is key to building successful and maintainable applications. 

One scenario that you can encounter is the need to trigger specific actions based on the size of the browser window. In this tutorial, we'll delve into the process of building a custom hook in React that allows you to execute actions when the window is resized to certain sizes. 

By the end of this guide, you'll not only have a powerful custom hook at your disposal but also a deeper understanding of hooks and their implementation. So let's get to it! 🙌🏼

---

### A reminder on custom hooks…

Custom hooks in React are reusable functions that allow you to extract common logic from your components and share it across your application, thus avoiding code repetition. 

They are named with the `use` prefix, and they can be used to encapsulate any kind of functionality, such as managing state, fetching data, or handling side effects. 

---

## 2. Prerequisites

Before we dive into building our custom hook, let's make sure we have the necessary tools and knowledge in place:

- Basic understanding of React hooks, particularly the `useEffect` hook (You can read [this other article of mine](https://dev.to/alais29/demystifying-the-useeffect-hook-in-react-a-beginners-guide-1mlo) if you need a refresher 🙂)
- Node.js and npm (Node Package Manager) installed on your system.

## 3. Setting Up the Project

To get started, let's set up a new React project 🙌🏼 or you can also use an existing one where you want to implement the custom hook. We're going to create one from scratch for the purposes of this guide. 

Open your terminal and run the following commands:

```
npm create vite@latest window-resize-hook-demo -- --template react
cd window-resize-hook-demo
npm run dev
```
This will create a new react project with Vite and run it, feel free to change the name if you want to 🙂

## 4. Building the Custom Hook Step by Step

### 4.1 Creating the hook

Create a “hooks” folder in the “src” folder and inside it create a file called “useWindowResize.jsx”. Technically you can place the hook wherever you want and call it whatever you want, but it's good practice to do it like this!

In the new file, let’s create our function:

```jsx
export const useWindowResize = (callback, screenSize) => {
  // Hook implementation
};
```

The `useWindowResize` hook will take 2 parameters:

- `callback`: The function that you want to execute when the window size falls outside the specified range.
- `screenSize`: An object containing `minWidth` and/or `maxWidth` properties, we'll use them to define the range of window sizes outside of which the callback will be triggered.

### 4.2 Handling the Resize Event

The first step is to handle the window resize event and check whether the current window size falls outside the specified range:

```tsx
import { useEffect } from 'react';

export const useWindowResize = (callback, screenSize) => {
  const { minWidth, maxWidth } = screenSize;
  useEffect(() => {
    const handleResize = () => {
      if (
        (typeof maxWidth !== "undefined" && window.innerWidth > maxWidth) ||
        (typeof minWidth !== "undefined" && window.innerWidth < minWidth)
      ) {
        callback();
      }
    };

    // add event listener
    window.addEventListener("resize", handleResize);

    // cleanup function
    return () => {
      // remove event listener
      window.removeEventListener("resize", handleResize);
    };
  }, [callback, maxWidth, minWidth]);
};

```

In this step, we add the `useEffect` hook because we have to add the `resize`  event listener to the window object so that we can determine when to execute our callback function. 

We do it inside the `useEffect` hook because then we can add a cleanup function to remove the event listener once the component where the custom hook is called is unmounted, otherwise the event listener will continue to be added to the window object, leading to memory leaks and/or unexpected behaviors.

We define the `handleResize` function inside the `useEffect` , which will be called when the event listener is triggered, that is, when the window is rezised. This function checks if the current window width is outside the specified range, if it is, the provided `callback` function is executed.

Notice that we check if the `maxWidth` and `minWidth` are undefined, so we can make those properties optional, this way you can either pass them both or just one of them. For example, if you only pass the `maxWidth`, then the callback will only execute when the window width is higher than the `maxWidth`.

We also add `callback`, `maxWidth` and `minWidth` to the `useEffect` dependency array, because if, for any reason, they change, we want the code inside the `useEffect` to run again to adapt to the new values.

### 4.3 Adding Debounce for Performance

To enhance the performance of our custom hook, we can add a debounce mechanism to the resize event listener. Debouncing prevents the callback from being executed too frequently during rapid resizing, leading to smoother performance. 

We'll use the `lodash.debounce` library for this purpose, that way we don’t have to install the complete lodash library just for this functionality, or if you have a project that’s already using lodash, no need to install this one.

First, install the library by running the following command:

```bash
npm install lodash.debounce
```

Now, import the debounce function and use it to wrap the `handleResize` function:

```tsx
import { useEffect } from 'react';
import debounce from 'lodash.debounce';

export const useWindowResize = (callback, screenSize) => {
  const { minWidth, maxWidth } = screenSize;
  useEffect(() => {
    // wrap it here
    const handleResize = debounce(() => {
      if (
        (typeof maxWidth !== "undefined" && window.innerWidth > maxWidth) ||
        (typeof minWidth !== "undefined" && window.innerWidth < minWidth)
      ) {
        callback();
      }
    }, 300);

    window.addEventListener("resize", handleResize);

    return () => {
      window.removeEventListener("resize", handleResize);
    };
  }, [callback, maxWidth, minWidth]);
};

```

In this case I established a 300ms delay. This means that when the window is resized, it will wait for 300ms after the last window resize event to call the `handleResize` function, that way we improve performance given that the function won’t be called on every second/milisecond that the window is being resized, only when it stops being resized for at least 300ms (Hopefully that made sense 😅).

## 5. Using the Custom Hook

With our custom hook `useWindowResize` now fully developed, it's time to put it to use in our React components. Import the hook into any component where you want to trigger specific actions based on window resize events:

```jsx
import { useWindowResize } from "./hooks/useWindowResize";

function App() {
  const handleWindowResize = () => {
    // Perform your desired actions here
    console.log("Window resized outside the specified size range");
  };

  useWindowResize(handleWindowResize, { minWidth: 768, maxWidth: 1024 });

  return (
    <>
      {/* Your component's content */}
    </>
  );
}

export default App;
```

In this example, the `handleWindowResize` function will be executed whenever the window width falls outside the range of 768 to 1024 pixels.

---

You might be wondering, what’s an example situation where I might need this? 🤔 Imagine you have a page that shows a list of products and a list of filters. 

On mobile window sizes, the filters are shown on a modal when the user clicks a button, but on desktop there’s no button because the filters are shown by default on the page (not on a modal). 

You might think you can handle it with just CSS, show the button that opens the modal on mobile and hide it on desktop, easy peasy ✨ But what if (and I know that this is an unlikely situation, but it could happen) an user on desktop resizes the window to a mobile size, opens the modal and then resizes it back to desktop? 

The button won’t show but the modal will still be open, so with the hook you could simply call a function to close the modal on desktop size and it’ll work like a charm 🙌🏼

And besides solving the issue you know have a reusable hook that you can use anywhere else in the project where you need a similar functionality 👩🏼‍💻

---

## 6. Conclusion

Congratulations! You've successfully built a powerful custom hook in React that enables you to execute actions when the window is resized to certain sizes. By encapsulating this functionality in a custom hook, you've created a reusable and modular piece of code that can enhance the user experience of your applications 🚀

If you have any other examples on where you could find this custom hook useful or if you have any feedback, feel free to leave a comment! 😄

Happy coding! 🤘🏼