Building CampaignHawk: Connecting Modals with React (Part 8)

Now that we have a nice-looking modal with some input fields, we need to come up with a way to hide and launch the modal when we want to. This is really where React shines.

Now we should give some thought to what component is really in charge of showing the modal. It would have to be a parent of Modal, which really just leaves us with Map. So that means we need to keep track of the state of our modal, which we’ll call showModal within Map.

We want the state of showModal to initially be false because we don’t want to start out with a modal.

getInitialState() {
return {
showModal: false
}
},

Now let’s look into what components need access to the state of our modal. The sidenav doesn’t care about the state of the modal. Only our Modal component cares about the state of the modal; so let’s pass this.state.showModal into our Modal component as props:

<Modal showModal={this.state.showModal} />

So let’s head over to our Modal component and think about what that component wants to do with the value in showModal. If it’s truthy, then it wants to change visibility to visible. Otherwise, it wants to make the modal hidden. We can do this with inline styling, which takes in an object as you may recall from a previous article.

render() {
let modalStyle = {
visibility: "hidden"
}
if (!!this.props.showModal) {
modalStyle.visibility = "visible"
}

We’re setting the default style to hidden, but if value of this.props.showModal is truthy (“!!” is saying “what is not not the boolean value of this.props.showModal?”, so if the value is anything other than false or any falsy value, it will return true.)

Then we need to set that style in our outermost modal container, modal-active-darken.

<div className="modal-active-darken" style={modalStyle}>

So now the visibility of our modal is controlled by our modalStyle. But how do we let the Modal component know what content to render? We will come back to that later.

Let’s go back to the Map component and give some thought to our showModal function. What component needs access to the showModal function? We want to trigger the modal when someone clicks on one of the specified icons in the sidenav, so what we want to do is pass showModal down through Sidenav and into SidenavIcons.

<Sidenav showModal={this.showModal} />

…And:

<SidenavIcons
showModal={this.props.showModal}
...

And now all we have to do is set an onClick event on our icons. Then we want to bind it to the item and pass the item.description (which is a unique value) as the new state for showModal.

<li key={item.name}
onClick={this.props.showModal.bind(null, item.description)}
..

So now we have a way to change the state of showModal all the way back up in our parent Map component, and we’re changing the value of showModal to the description (a string) of the icon that was clicked.

Now let’s go back over to Modal component and think about what value of showModal is being passed and how we can best utilize that value. The easiest way to do this is probably with a self-invoked anonymous function and a switch statement. Doing it this way allows us to write the logic inline and view all of our conditionals in a nice list:

<div className="modal-container">
{(() => {
switch (this.props.showModal) {
case "Add Volunteer": return <AddVolunteerModalContent />;
case "View Volunteers": return <h1>view volunteers</h1>;
default: return false;
}
})()}

</div>

What we have here is a switch statement that evaluates this.props.showModal and returns the proper content. For the time being, I have placeholder value for “View Volunteers”.

The last thing we need to do is add the ability to hide our modal. If we think of all the places that we might need access to the hideModal function, we’ll find that they’re all neatly contained in our Modal component. So, let’s pass our hideModal function into Modal.

<Modal 
showModal={this.state.showModal}
hideModal={this.hideModal} />

And then within our Modal component, we’re going to have to think about where we want to put events that trigger a modal close. We definitely want it within our modal content components, so let’s pass that in now:

case "Add Volunteer": return <AddVolunteerModalContent 
hideModal={this.props.hideModal}/>;

Then in our close modal “x”, let’s add a click event:

<div className="close-modal-x" 
onClick={this.props.hideModal}>&times;</div>

Let’s go ahead and add that to our buttons too:

<button 
onClick={this.props.hideModal}
className="button">Submit</button>
<button
onClick={this.props.hideModal}
className="button-flat">Discard</button>

Now that we can open and close the modal, we should add a transition so it fades in and out. The best way to do that is with opacity. First we should add a transition to our modal-active-darken class:

transition: all 0.3s ease;

Then we should add an opacity property to our inline styling.

let modalStyle = {
visibility: "hidden",
opacity: "0"
}
if (!!this.props.showModal) {
modalStyle.visibility = "visible"
modalStyle.opacity = "1"
}

That should do it for our first modal!

Next Steps

We could build out another piece of content for our modals and get some cool looking fake data from Faker, but that would just be more of the same. The next step is probably to start integrating a map.