How to Make Your React Apps Responsive with a Custom Hook
Tags: react, tutorial, css, programming
Published: 2021-10-02 09:11:32

## Overview

The hook I'm going to teach you today, despite being very simple, it's possible to do several things with it. For example if you want a certain component to have a certain behavior from a certain resolution, you can use this hook. 

However, you can also make responsiveness adjustments with it in a simple and fast way.

The main reason I created this hook is that I needed to access the current width of the page, because I was using a calendar and I wanted it to adapt its behavior from a certain resolution because it was not responsive with the breakpoints from the css framework I was using.

And for that I decided to share with you the code of this hook as I will show you a simple example. Last but not least I will make the code for this example available in a github repository.

# Let's code

Today I'm only going to install one dependency, but it's not mandatory, this is just to make my work in this example easier.

```sh
npm install classnames
```

Now let's immediately start working on our hook.

```js
// @src/hooks/useMediaQuery.js

const useMediaQuery = () => {
  // ...
};

export default useMediaQuery;
```

Our hook will take a single argument which will be the min width of the page, which will be our target.

```js
// @src/hooks/useMediaQuery.js

const useMediaQuery = (minWidth) => {
  // ...
};

export default useMediaQuery;
```

Then we will import the `useState()` hook so that we can store two properties in our state, the windowWidth and the isDesiredWidth.

```js
// @src/hooks/useMediaQuery.js
import { useState } from "react";

const useMediaQuery = (minWidth) => {
  const [state, setState] = useState({
    windowWidth: window.innerWidth,
    isDesiredWidth: false,
  });

  // ...
};

export default useMediaQuery;
```

The idea of windowWidth is to store the with of the current window, while isDesiredWidth has to be a boolean to validate that the window's current width is less than minWidth (our target).

Next, let's import the `useEffect()` hook to be aware of the changes that are made, which in this case is the change and validation of the width of the window.

```js
// @src/hooks/useMediaQuery.js
import { useEffect, useState } from "react";

const useMediaQuery = (minWidth) => {
  const [state, setState] = useState({
    windowWidth: window.innerWidth,
    isDesiredWidth: false,
  });

  useEffect(() => {
    // ...
  }, [state.windowWidth]);

  // ...
};

export default useMediaQuery;
```

Now we will have to create a function that will be our handler, which will be used whenever the window resizes.

```js
// @src/hooks/useMediaQuery.js
import { useEffect, useState } from "react";

const useMediaQuery = (minWidth) => {
  const [state, setState] = useState({
    windowWidth: window.innerWidth,
    isDesiredWidth: false,
  });

  useEffect(() => {
    const resizeHandler = () => {
      // ...
    };
    // ...
  }, [state.windowWidth]);

  // ...
};

export default useMediaQuery;
```

In this handler we'll get the width of the current window, then we'll compare it with our minWidth and finally we'll store the data obtained in our state.

```js
// @src/hooks/useMediaQuery.js
import { useEffect, useState } from "react";

const useMediaQuery = (minWidth) => {
  const [state, setState] = useState({
    windowWidth: window.innerWidth,
    isDesiredWidth: false,
  });

  useEffect(() => {
    const resizeHandler = () => {
      const currentWindowWidth = window.innerWidth;
      const isDesiredWidth = currentWindowWidth < minWidth;
      setState({ windowWidth: currentWindowWidth, isDesiredWidth });
    };
    // ...
  }, [state.windowWidth]);

  // ...
};

export default useMediaQuery;
```

Now we have to create an event listener where whenever the window does a resize we want to invoke our resizeHandler function.

And if the window doesn't resize, we'll cleanup our `useEffect()` hook and remove the event listener.

```js
// @src/hooks/useMediaQuery.js
import { useEffect, useState } from "react";

const useMediaQuery = (minWidth) => {
  const [state, setState] = useState({
    windowWidth: window.innerWidth,
    isDesiredWidth: false,
  });

  useEffect(() => {
    const resizeHandler = () => {
      const currentWindowWidth = window.innerWidth;
      const isDesiredWidth = currentWindowWidth < minWidth;
      setState({ windowWidth: currentWindowWidth, isDesiredWidth });
    };
    window.addEventListener("resize", resizeHandler);
    return () => window.removeEventListener("resize", resizeHandler);
  }, [state.windowWidth]);

  // ...
};

export default useMediaQuery;
```

Then just return our isDesiredWidth so we can use the hook.

```js
// @src/hooks/useMediaQuery.js
import { useEffect, useState } from "react";

const useMediaQuery = (minWidth) => {
  const [state, setState] = useState({
    windowWidth: window.innerWidth,
    isDesiredWidth: false,
  });

  useEffect(() => {
    const resizeHandler = () => {
      const currentWindowWidth = window.innerWidth;
      const isDesiredWidth = currentWindowWidth < minWidth;
      setState({ windowWidth: currentWindowWidth, isDesiredWidth });
    };
    window.addEventListener("resize", resizeHandler);
    return () => window.removeEventListener("resize", resizeHandler);
  }, [state.windowWidth]);

  return state.isDesiredWidth;
};

export default useMediaQuery;
```

Now let's create the styles for our App.jsx module:

```css
/* @src/App.module.css */

.layout {
  display: flex;
  justify-content: center;
  align-items: center;
  height: 100vh;
  width: 100vw;
  background-color: #f1f6f9;
}

.layout h1 {
  color: #14274e;
}

.mobile {
  background-color: #394867 !important;
}

.mobile h1 {
  color: #f1f6f9;
}

.tablet {
  background-color: #14274e;
}

.tablet h1 {
  color: #9ba4b4;
}
```

Now with our hook created and our App.jsx styles created, we can import both into the component. Then we'll use our hook to determine if it's mobile or tablet. In jsx we will do conditional rendering to have the desired visual result at each of the resolutions.

```js
// @src/App.jsx
import React from "react";
import classNames from "classnames";

import styles from "./App.module.css";
import useMediaQuery from "./hooks/useMediaQuery";

const App = () => {
  const isMobile = useMediaQuery(768);
  const isTablet = useMediaQuery(1024);
  return (
    <div
      className={classNames([
        styles.layout,
        isMobile && styles.mobile,
        isTablet && styles.tablet,
      ])}
    >
      <h1>{isMobile ? "Mobile" : isTablet ? "Tablet" : "Desktop"}</h1>
    </div>
  );
};

export default App;
```

You should get a result similar to this:
![final app](https://res.cloudinary.com/dj5iihhqv/image/upload/v1633163873/Kapture_2021-10-02_at_09.36.13-min_u6u32k.gif)

As promised at the beginning of the article, to access the github repository click [here](https://github.com/FranciscoMendes10866/responsive-hook).

## Conclusion

As always, I hope you found it interesting. If you noticed any errors in this article, please mention them in the comments. 🧑🏻‍💻

Hope you have a great day! 👹