Introduction
In this article, I will be building a simple modal which will be triggered by a Call-to-action (CTA) button. To hide the modal, the “X” button is clicked. This is basically removing the CSS Display value of "none" when the CTA button is clicked and adding it back when the close button is clicked.
Click on the “Sign In” button to see the modal.
To make the styling easier I will be using Tailwind CSS. Tailwind CSS is a framework that makes styling a lot better. Kindly ignore the classes if they are a lot and I would leave a link to learn Tailwind CSS in the resources section below. It is pretty easy to understand, I bet you will like it. 😀
Prerequisite
- To understand this article better, you should know JavaScript to an extent.
- This article would be best understood by those with prior knowledge of React.js. For beginners, I would advise checking the resources section for free course(s).
- Also, knowledge on Functional components and Conditional rendering is needed.
Now, let’s get started…
Building the UI
As shown in the code below, I will use our Header.js as the Parent component and the Modal.js will be the Child component.
import React from "react";
import Modal from "./Modal";
const Header = () => {
return (
<header className="bg-gray-700 text-white p-4">
<div className="flex justify-between items-center">
<p className="font-bold text-2xl">George Inc.</p>
<ul className="flex text-xs md:text-sm font-light">
<li className="hover:underline pr-3">Home</li>
<li className="hover:underline pr-3">About Us</li>
<li className="hover:underline pr-3">Blog</li>
<li className=" hover:underline pr-3">Contact Us</li>
</ul>
<button
className="bg-white text-gray-700 rounded p-2"
>
Sign In
</button>
</div>
<Modal />
</header>
)
}
export default Header;
The Header component will hold the CTA button and the Modal component will come in as a Child component.
Also, the red button on the modal will serve as the close button.
import React from "react";
const Modal = () => {
return (
<div
className="w-screen h-screen flex flex-col items-center justify-center bg-white text-gray-700 absolute top-0 left-0 p-4"
>
<div className="w-full md:w-1/2 lg:w-1/3 shadow-xl border rounded relative p-3">
<p className="font-bold text-xl"> Sign In</p>
<button
className="bg-red-500 text-white border border-red-500 rounded-full absolute top-0 right-0 py-1 px-2 m-2"
>
x
</button>
<form>
<input
type="text"
placeholder="Enter username"
className="hover:border-gray-700 focus:border-gray-700 border-b p-2 my-2"
/>
<br />
<input
type="password"
placeholder="Enter password"
className="hover:border-gray-700 focus:border-gray-700 border-b p-2 my-2"
/>
<br />
<button className="bg-gray-700 text-white hover:shadow-lg rounded p-2 mt-2">
Login In
</button>
</form>
</div>
</div>
)
}
export default Modal;
Notice that the Modal component has an absolute class and with that, the header component will hide when the Modal component comes up.
Connecting the Child and Parent component
Now for the Modal to work properly, I need a State which will house a boolean value and play the part of an “on-off switch”.
There are three ways to which this can be done, namely:
- Putting the State in the Child component, then using Ref and forwardRef to make the Parent component have access to the Child components State.
- Making the Parent component have a State for the Child component, then passing the State to the Child component as a prop.
- Lastly, making the State to be in a React Context. This way, both Parent and Child components will have direct access to the State as a React Context.
In this article, I will cover the second way which is making the state to live in the Parent component. This is an easier way of doing things if the components are in a Parent-Child format.
Adding the State to the Parent component
Moving forward, I added the State in the Header (Parent) component like so :
const [ state, useState ] = React.useState(false);
I also added a function that will change the State from False to True and vice versa when it is called on.
const SwitchState = () => {
useState(!state);
};
When the above function is called? It changes the State from false to true or from True to False. This means it can be used on the Close button in the Modal component and the CTA button in the Header component.
With the above State and Function in the Parent I can now :
- Add the function to the CTA button as seen below
onClick={SwitchState}
- Add the State and function to the Modal component in the Header component.
<Modal modal={state} toggleModal={SwitchState} />
Moving over to the Modal component, I can now :
- Create a handleClick Function to call the SwitchState Function I am passing as a prop.
const handleClick = () => {
props.toggleModal();
}
- Add the handleClick Function to the Close button
onClick={handleClick}
Because I want the modal to be hidden on the loading of the app, the State in the Parent(Header) component is False by default. Using this State, I can now render our modal conditionally by replacing the Flex class with this
`... ${modal ? "flex" : "hidden"} ...`
The above is a ternary condition, and the final outcome will be this
<div className={`w-screen h-screen ${props.modal ? "flex" : "hidden"} flex-col items-center justify-center bg-white text-gray-700 absolute top-0 left-0`} >
...
</div>
With the above code inserted, the modal becomes hidden.
Using this method I can now see that on clicking of the CTA button, the Modal component pops up and on clicking of the red-close button on the Modal, the Modal component hides.
Testing
To understand this better, I will add a console.log(modal) to the Function in the Parent component and console.log(props.modal) to the Function in the Child component like so:
const SwitchState = () => {
useState(!state);
console.log(state);
}
and
const handleClick = () => {
props.toggleModal();
console.log(props.modal);
}
This Logs out the State whenever the Functions are triggered, which will show the State moving from False to True and True to False.
The final result is found below ( Code ).
Conclusion
Putting the State of a Functional Child component in its Parent component is better than using the Ref and forwardRef. If the functional components are equal in the hierarchy and are not playing the “Parent-Child” component structure, then using the React Context and hooks will be the best option.