Table of Contents
Initial project
We start with a simple create-react-app 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)
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:
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:
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>
);
}
Using custom hooks to easily show and hide modals
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()
):
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.
What is a custom hook?
React Hooks are powerful tools introduced in React 16 and we've already used them above to create our only state(showModal
). Custom Hooks 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
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
:
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:
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:
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:
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:
Use React Portal
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:
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.
What is React Portal?
React Portals render our component outside the DOM hierarchy of the parent component. Here is how we use it:
ReactDOM.createPortal(child, container);
And now let's see how we use it in our Modal.
Use create Portal
This part is so simple. The only thing we have to do is to wrap our modal with a portal:
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:
Final Project
You also can find the final code in this repository
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
Top comments (7)
Nice modal, I think using portal is the way to go unless you can use dialog.
I have a couple of things to mention though.
I have a couple of posts on a similar topic if you want to have a look.
React: Using portals to make a modal popup
Andrew Bone ・ Aug 7 '20
React: Using native dialogs to make a modal popup
Andrew Bone ・ May 19 '22
Amazing points Andrew and sorry about the buttons 😅 I'll try to make some changes. Thanks 🙏
Nice tutorial! Is there a reason you chose not to use
useReducer
?I think it's the better option for things like modals.
That's actually a great idea @kachiic . But I thought if I do that I have to change the topic and description a little bit and that would complicate things. Maybe as the next topic.
Great idea though ✌️
Pretty good tutorial.
Thank you 🙏 I’m thrilled to hear that
Nice content!
I have a question. What if I have more than one modal on the page, won't it create conflict between them?