Image for post
Image for post

Create a root-level modal store in React Native

Michael Lefkowitz
Jan 8, 2020 · 8 min read

In the original design and flows of our React Native apps, we utilized the native Alert for a few different needs — but primarily they were error, confirmation, and success notifications.

These worked well in most cases for the past year for most every case we had ever needed them — the only issue we ever had was a lack of styling flexibility, so items in a list for instance would end up center aligned on iOS, which was less than ideal.

Success notifications were used most often — the alerts would usually appear following a successful API response after filling out a form, which historically had been built as single screens. When these success notifications were triggered, we would also kick our navigation back a screen or two so our users would be where we wanted them.

The problem

As our design team begun to share mocks of screens with us that had a fresh coat of paint on them, they also had a major change in many of our original flows. The idea is to move away from scrolling, single screen forms to a more mobile-friendly, multi screen flow. In addition, these new designs replaced our native alerts with full screens that was more consistent with our branding and enabled us to style them with greater flexibility.

While our engineering team liked the look of these new flows and success screens, replacing our native alerts with them proved to be more work than we expected — by making the alert into a separate screen, we now lost the ability to trigger a navigation “behind the scenes” while the user was interacting with our alert. And, since we were also moving from single screen to multi screen forms, we couldn’t simply replace the current screen in the stack with a success screen.

The solution

Because of this “behind the scenes” magic we were introducing, we wanted to avoid having the modal as part of our navigation stack completely. To handle this, we created a modal store within Redux and a Modal component at the root of our app — which we’ll walk through the basics of how to create in this tutorial. This same pattern could be applied to your choice of state management solution if Redux doesn’t work for you.

To start, we’ll initialize a new React Native project. Then, add React Navigation with a couple dummy screens and Redux. In our Redux store, we’ll setup a modal reducer that has an id field. I used the React Native CLI to start, but you may be able to simplify things by utilizing Expo. To see what our project looks like so far, check it out here.

Great, now that we have a baseline project setup, let’s discuss the main steps involved in building this out:

  1. Create a connected-RootModal component at the root of our App
  2. Create unique views within the parent Modal
  3. Let the store drive the visibility of the RootModal (and specific view)
  4. Add support for props

Alright, let’s get started.

1. Create a connected-RootModal component at the root of our App

First step, we’ll create a file called RootModal.js that will look like this:

Here, we’ve created a very simple connected-component that displays a native Modal with a button at the bottom.

Then, we will import it into our App.js and drop it in.

Now when we refresh our app, we should see something like this:

Exactly what we want, a root-level Modal that covers up the rest of our app. Click here to see what our project currently looks like.

2. Create unique views within the parent Modal

Next, we’re going to create some unique views / components that the root modal can display. For simplicity sake in this tutorial, we aren’t going to make anything too fancy — but it should be enough to spark your understanding of the concept.

Let’s make two new files — Success.js and Error.js

Now, we want to import them into our RootModal file and test out displaying them — so we’ll replace that placeholder empty View we had.

Let’s take a look and see what this looks like now when using Modals.Success and Modals.Error

Perfect. Click here to see what our project currently looks like.

3. Let the store drive the visibility of the RootModal

Now for the fun part. What we want to do setup our store to set and unset a specific id. Our RootModal component will then look at that id and then determine which modal to display. If the id is empty, no modal will be shown.

First, we’ll create a ModalActions.js and create two actions — one that will set an id and another that will clear it out.

Then, we’ll update our ModalReducer to support these actions:

Next, we need to update our RootModal. First we need to add mapDispatchToProps to our Redux-connection and import our new hideModal action. Then, we'll create a constant that assigns to a component based on the id in our store and update the native Modal component to be displayed only if that id is truthy. And finally, inject the custom view into the Modal. All together, it should look like this:

Lastly, we’ll update one of our screens to support our new showModal action and add a couple buttons to support displaying our RootModal.

All wrapped up — here’s what this looks like (and here’s our project at its current stage):

4. Add support for props

How can we make this pattern a little more flexible? For one, we should setup an additional reducer that takes in some custom props that will be passed through to our custom views. All we have to is create the action and reducer to support it and spread those props in on our RootModal.

To handle this, we’ll first update our showModal and hideModal actions

Then, we’ll add a modalProps reducer to our ModalReducers file

Next, we’ll modalProps to our mapStateToProps in the RootModal file and spread those props in:

And finally, when we are calling our showModal action, we'll be able to pass along any additional props that may be relevant, and update those unique views to support them.

Our project now looks like this, and here’s a preview of how everything looks all wrapped up:

Final thoughts

Hopefully this tutorial has given you enough context to help use this concept as a starting point for however you may need to utilize a custom store-backed root-level modal. In our apps, our RootModal is a little more “dumb” than this example — and we leave all actions, including closing, to the Redux-connected inner views. Also, we eventually ran into an issue related to this long-standing bug when building out this pattern — which led us to replace our use of a native Modal with the react-native-root-modal library. Hopefully you don’t need that extra step, but it is worth mentioning.

LawnStarter Engineering

Notes from the LawnStarter Engineering Team

Medium is an open platform where 170 million readers come to find insightful and dynamic thinking. Here, expert and undiscovered voices alike dive into the heart of any topic and bring new ideas to the surface. Learn more

Follow the writers, publications, and topics that matter to you, and you’ll see them on your homepage and in your inbox. Explore

If you have a story to tell, knowledge to share, or a perspective to offer — welcome home. It’s easy and free to post your thinking on any topic. Write on Medium

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store