Hi guys !
Last week, we talked about 5 Customs React Hooks ! 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!
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. The goal is to include any content to the modal and to manage the opening and closing of the component.
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 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.
And for our component to be inserted in this #modal-app
div, let's use the createPortal()
method offered by ReactDOM.
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
So let's include this method for our component like this:
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.
- isOpen
true
=== Modal opened - isOpen
false
=== Modal closed
We use the conditionally rendering with the JavaScript conditional operator.
Then, in order for the parent to change this local state, you'll have to call up the "refs".
This is where useRef, useImperativeHandle and forwardRef 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.
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:
The CSS !
You can find the css code here.
Usage
- Demo: https://react-modal.viclafouch.vercel.app
- Source code: https://gist.github.com/viclafouch/6ee36b2cb7d28484d20f05e68b3433f9
Voilaaa ! Feel free to add your own modifications !
Cheers
Top comments (10)
When I call modal.current.open() from the onClick() handler I'm getting that modal.current is null. Any idea what could be the reason?
const ObjList = () => {
const modal = useRef(null);
return (
Hello World
...
)
}
Super article, what I would like to know is how to launch the modal dialog after a call to a redux action ( from a button click )
In other words, the user is modifying something, they click the “save” button, the call to the API is made, and THEN the modal is shown such as “successfully saved”
HEy how can I send propr to the modal? lets say that I want to zoom a img from the parent to a modal(child) how can I pass the img as a prop?
Nice one. I always prefer that modal's manages its own visibility state. Also in IMHO excellent example of using
useImperativeHandle
hook.ok i have a question for u . if we using state every change make all components in that rerender.
and then we can forward a function. way u forward a ref instead a function.
less render? or more speed ? what ?
can I use your code for production and personal purposes ?
Yes :)
Hi, So can you only open this modal from the same component where the click button to open it is? the implementation isn't really working.
Nop, just forward your ref to your component.. Or better, share it by using the Context API. There are always solutions ;)
Thank you for sharing! I was struggling to figure out how to build reusable modal components with React and this helped 👍🏻