Two best practices of creating modals in React 18
Tags: react, webdev, javascript, tutorial
Published: 2022-07-18 22:32:08

### Table of Contents

- **[Initial project](#initial-project)**
- **[Using custom hooks to easily show and hide modals](#using-custom-hooks-to-easily-show-and-hide-modals)**
  - [What is the problem?](#what-is-the-problem-custom-hook)
  - [What is a custom hook?](#what-is-a-custom-hook)
  - [Rewriting our Modal using a custom hook](#rewriting-our-modal-using-a-custom-hook)	
- **[Use React Portal](#use-react-portal)**
  - [What is the problem?](#what-is-the-problem-portal)
  - [What is react Portal](#what-is-react-portal)
  - [Use create Portal](#example)
- **[Final Project](#final-project)**

---
## Initial project
We start with a simple [create-react-app](https://reactjs.org/docs/create-a-new-react-app.html) project which only has two components, one is our main component which is App, and the other is Modal component.(This project also can be found in [this Github repo](https://github.com/moghanbari/modal-best-practices-before))


{% embed https://codesandbox.io/embed/modal-best-practices-initial-0jypz5?fontsize=14&theme=dark&view=preview&view=editor&hidenavigation=1 %}


As you can see, Modal component uses a flag(`show`), which comes from parent component as a prop, to check if it should gets rendered or not. It also sends a click event of close button to the parent component to let the parent know it's time to toggle the flag:

```react
const Modal = ({ show, onCloseButtonClick }) => {
  if (!show) {
    return null;
  }

  return (
    <div className="modal-wrapper">
      <div className="modal">
        <div className="body">Click on the close button to close the modal.</div>
        <div className="footer">
          <button onClick={onCloseButtonClick}>Close Modal</button>
        </div>
      </div>
    </div>
  );
};

export default Modal;

```
In the App component however, a state is being used to pass as a prop to Modal. Additionally, App is listening to click event of close button in Modal and set the state/flag to false whenever click event happens or in other words, close the modal:

```react
function App() {
  const [showModal, setShowModal] = useState(false);

  const openModal = () => {
    setShowModal(true);
  }

  const closeModal = () => {
    setShowModal(false);
  }

  return (
    <div className="App">
      <Modal show={showModal} onCloseButtonClick={closeModal} />
      <div className="button" onClick={openModal}>Open Modal</div>
    </div>
  );
}
```

&nbsp;
&nbsp;

## Using custom hooks to easily show and hide modals

&nbsp;
<a name="what-is-the-problem-custom-hook"></a>
### What is the problem?
The problem with this method though is whenever you need to use the Modal, you have to repeat yourself by adding one state as the flag(`showModal`), and two methods which are responsible for toggling the state(`openModal()` and `closeModal()`):

```react
const [showModal, setShowModal] = useState(false);

const openModal = () => {
  setShowModal(true);
}

const closeModal = () => {
  setShowModal(false);
}
```

However, in this section we'll see that by using a simple custom hook, you are no longer required to add this bunch of repeated code.

&nbsp;
### What is a custom hook?
[React Hooks](https://reactjs.org/docs/hooks-intro.html) are powerful tools introduced in React 16 and we've already used them above to create our only state(`showModal`). [Custom Hooks](https://reactjs.org/docs/hooks-custom.html) in the other hand, simply are hooks created by you, and you can use them to extract components logic into reusable functions.

Based on official definition:

> **A custom Hook is a JavaScript function whose name starts with ”use” and that may call other Hooks**

&nbsp;
### Rewriting our Modal using a custom hook
As I mentioned earlier, in our project, we're repeating the logic of using Modal, and now we can use our custom hook to keep the logic.

**Step 1: Create**
To name our custom hook, we already know that a custom hook's name **must** start with `use` and as we are creating this custom hook for our Modal, we can simply call it `useModal`:

```react
const useModal = () => {
	
}

export default useModal;

```

**Step 2: Add**
We already know about the repeated logic for modal, we need a state and a function to toggle it. As this logic is already exists in App component, we can simply cut and paste it into our custom hook:

```react
import { useState } from 'react';

const useModal = () => {
	const [isShowing, setIsShowing] = useState(false);

	function toggle() {
		setIsShowing(!isShowing);
	}
}

export default useModal;

```

**Step 3: Return**
Now we should make the state and toggle function accessible outside of our custom hook, and because we have two things to return, like the standard useState hook in React library, we can simply return values as an array:

```react
import { useState } from 'react'

const useModal = () => {
	const [isShowing, setIsShowing] = useState(false);

	function toggle() {
		setIsShowing(!isShowing);
	}

	return [
		isShowing,
		toggle
	];
}

export default useModal;
```

**Step 4: Use**
Now it's time to use our gorgeous custom hook wherever we need to use the Modal component:

```react
function App() {
  const [isShowingModal, toggleModal] = useModal();

  return (
    <div className="App">
      <Modal show={isShowingModal} onCloseButtonClick={toggleModal} />
      <div className="button" onClick={toggleModal}>Open Modal</div>
    </div>
  );
}
```

And here is the result:

{% embed https://codesandbox.io/embed/modal-best-practices-with-custom-hook-r7yx06?fontsize=14&theme=dark&view=preview&view=editor&hidenavigation=1 %}

&nbsp;

## Use React Portal
&nbsp;
<a name="what-is-the-problem-portal"></a>
### What is the problem?
There is still one more thing that we can improve in our Modal. Here is how Modal gets render in the DOM:


![Modal is inside app element in the DOM](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/rksl9ep77g0dyr327wge.png)

As you can see, the Modal appears inside `<div class="App">` simply because we put the modal inside the App component. However, does it really belong there? A modal usually considered a component that belongs outside the DOM hierarchy.

&nbsp;
### What is React Portal?
React [Portals](https://reactjs.org/docs/portals.html) render our component outside the DOM hierarchy of the parent component. Here is how we use it:

```react
ReactDOM.createPortal(child, container);
```

And now let's see how we use it in our Modal.

&nbsp;
### Use create Portal
This part is so simple. The only thing we have to do is to wrap our modal with a portal:

```react
import ReactDOM from 'react-dom';

const Modal = ({ show, onCloseButtonClick }) => {
  if (!show) {
    return null;
  }

  return ReactDOM.createPortal(
    <div className="modal-wrapper">
      <div className="modal">
        <div className="body">
          Click on the close button to close the modal.
        </div>
        <div className="footer">
          <button onClick={onCloseButtonClick}>Close Modal</button>
        </div>
      </div>
    </div>
    , document.body
  );
};

export default Modal;
```
Just keep in mind that the second parameter(`document.body`) is the rest of the DOM which you have to pass, otherwise you will get an error.

And here is how our Modal looks like inside our DOM after using Portal:

![Now Modal is outside of app's DOM hierarchy](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/a14dh651fwscggdo8am3.png)


&nbsp;

## Final Project
&nbsp;
{% embed https://codesandbox.io/embed/modal-best-practices-final-x9hx9k?fontsize=14&theme=dark&view=preview&view=editor&hidenavigation=1 %}

You also can find the final code in [this repository](https://github.com/moghanbari/modal-best-practices)

---

What do you think about this? Is there any other way that I haven't mentioned? Please please please let me know by commenting down below. I can't wait to hear that. Thanks