🔧 Build a complete Modal Component with React Hooks 🌈
Tags: react, javascript, showdev
Published: 2020-06-24 13:34:10

Hi guys ! 

Last week, we talked about [5 Customs React Hooks](https://dev.to/viclafouch/5-useful-and-modern-custom-hooks-for-your-react-app-3dl) ! Many of you have shared this article. So this week, let's continue with React and the implementation of a Modal component __without installing any packages__!

![React Modal demo](https://dev-to-uploads.s3.amazonaws.com/i/j3fxirarskmt4zriraj0.gif)

# A Modal component from scratch

Before starting, here are the elements we will use during this tutorial:

* __React Hooks__
* __Portals from ReactDOM__
* __A little bit of CSS__

Let's start with our `modal.jsx` (or `modal.js`) file !

## Our component:

Let's start with the basics: the creation of our [function component](https://reactjs.org/docs/hooks-state.html#hooks-and-function-components). The goal is to include any content to the modal and to manage the opening and closing of the component.

![React Modal Hooks](https://dev-to-uploads.s3.amazonaws.com/i/o6zjugob8jdmavt8l7lc.png)

Secondly, what we want is a modal that is independent of our application. We don't want to have any `z-index` concerns in css or any `overflow` concerns. So let's insert this component into a different location in the DOM. But how?

### Let's introduce the Portals from ReactDOM !

Portals provide a way to [render children in a DOM node that exists outside of the DOM component](https://reactjs.org/docs/portals.html) hierarchy, that is, outside of the `#root` or `#app` div of your React application.

First, let's add a new div to our `html` and give it a 'modal-app' ID.  React does not create a new div, but displays the children __in__ that `modal-app` div.

Note that the `#modal-app` can be any valid element of the DOM (`div`, `section`, `span`...), regardless of its position.

![React Modal HTML](https://dev-to-uploads.s3.amazonaws.com/i/km8hmmrw3u4nskxla57x.png)

And for our component to be inserted in this `#modal-app` div, let's use the `createPortal()` method offered by ReactDOM.

![React Modal ReactDOM](https://dev-to-uploads.s3.amazonaws.com/i/avkdtvkwbwd7mkb2m1go.png)

> Even though a portal can be anywhere in the DOM tree, it behaves like a normal React child in every other way. Features like context work exactly the same regardless of whether the child is a portal, as the portal still exists in the React tree regardless of position in the DOM tree.

Source : [Portals - React](https://reactjs.org/docs/portals.html)

So let's include this method for our component like this:

![React Modal Portals](https://dev-to-uploads.s3.amazonaws.com/i/fugkk9c0fz8pvsuinpjg.png)

### Open the Modal

Logically, it will be the parent component that will ask the modal to open. But how could we proceed?

First of all, let's add a local state to our __Modal__ component in order to know if the modal is open or not.

`useState` allows us to create our state with a boolean value. A `defaultOpened` prop will tell us if we want to open the modal directly on create. `false` is the default value.

![Open React Modal](https://dev-to-uploads.s3.amazonaws.com/i/8dnvdvfjey4bdxcwrf3y.png)

* isOpen `true` === Modal opened
* isOpen `false` === Modal closed

We use the [conditionally rendering with the JavaScript conditional operator](https://reactjs.org/docs/conditional-rendering.html#gatsby-focus-wrapper).

![Conditionally rendering](https://dev-to-uploads.s3.amazonaws.com/i/7v3ikl9km5cw9asijlxm.png)

Then, in order for the parent to change this local state, you'll have to call up the "refs". 

This is where [useRef](https://reactjs.org/docs/hooks-reference.html#useref), [useImperativeHandle](https://reactjs.org/docs/hooks-reference.html#useimperativehandle) and [forwardRef](https://reactjs.org/docs/react-api.html#reactforwardref) come in.

To call `setIsOpen` from the parent component, we need to attach a reference to our __Modal__. 

* `useRef` is used to attach a reference to our modal.
* `forwardRef` is used to forward the reference to the __Modal__ component.
* `useImperativeHandle` is used to expose methods to the parent component.

![React Modal Opening](https://dev-to-uploads.s3.amazonaws.com/i/k07fcrsh524sfphdodk1.png)
 
### Close the Modal

Then, there are several ways to leave a modal.

* By __clicking outside the content__ of the modal.
* By __using the small cross (x)__ of our content. 
* By __using the ESCAP__ key of the keyboard.

First, let's capture the `keydown` event and check if the user use the ESCAP key of his keyboard. 

`useEffect` allowing us to create a listener on the `keydown` event __ONLY__ if the modal is open. If the user uses the ESCAP key on his keyboard, then the modal closes.

The result is:

![React Modal close](https://dev-to-uploads.s3.amazonaws.com/i/cluo5tzigi8aclr4xmr6.png)

## The CSS ! 

You can find the css code [here](https://gist.github.com/viclafouch/6ee36b2cb7d28484d20f05e68b3433f9#file-styles-css).

### Usage

![React Modal usage](https://dev-to-uploads.s3.amazonaws.com/i/31j1l86ir5cfqmy0if8h.png)

* Demo: [https://react-modal.viclafouch.vercel.app](https://react-modal.viclafouch.vercel.app)
* Source code: [https://gist.github.com/viclafouch/6ee36b2cb7d28484d20f05e68b3433f9](https://gist.github.com/viclafouch/6ee36b2cb7d28484d20f05e68b3433f9)

Voilaaa ! Feel free to add your own modifications !

Cheers

{% link https://dev.to/viclafouch/5-useful-and-modern-custom-hooks-for-your-react-app-3dl %}

{% link https://dev.to/viclafouch/a-full-stack-meme-generator-with-react-hooks-express-5dc7 %}