How to Create a Modal Route with React Router

How to Create a Modal Route with React Router

Building a modal module for react with react-router. First, go ahead and install react-router-dom through npm. At the very top level of your application,

Modals are very useful for displaying one view on top of another.

However, they are more than an absolutely positioned <div> element wrapping everything when it comes to implementation. Especially if you need dynamic URLs, page refreshes, or a simple scrolling interaction on a mobile device.

In this article, we’ll discuss the various aspects of modals and identify solutions to satisfy the requirements that come with creating dynamic URLs, page refreshes, and other features.

Before starting to shape the modal component, let’s start with some basics of the react-router package.

We’ll use four components from this package: BrowserRouter, Route, Link, and Switch.

Since this is not a react-router tutorial, I won’t be explaining what each of these components do.

However, if you’d like some info about react-router, you can check out this page.

Basic routing

First, go ahead and install react-router-dom through npm.

npm install react-router-dom --save

At the very top level of your application, use the <BrowserRouter/> component to wrap your app.

import { BrowserRouter } from "react-router-dom";

ReactDOM.render(
  <BrowserRouter>
    <App />
  </BrowserRouter>,
  document.getElementById('root')
);

Inside <App/>, you’ll need to specify the routes so that you can render a specific view when one of them — or none of them — match.

Let’s assume we have three different components to render: <Home/>, <About/> and <Contact/>. We’ll create a navigation menu, which will always be visible at the very top of the application.

The <Link/> or <NavLink/> components from react-router-dom are used for navigation purposes, while <NavLink/> has the special feature of being applicable to a specific styling when the current URL matches.

Functionality-wise, you can use either one.

Below is the basic structure of the navigation menu, which changes the URL accordingly:

render() {
  return (
    <div className="app">
      <div className="menu">
        <Link className="link" to='/'>Home</Link>
        <Link className="link" to='/about'>About</Link>
        <Link className="link" to='/contact'>Contact</Link>
      </div>
    </div>
  );
}

The next thing we’ll do is implement the mechanism that matches the URL and renders a specific component.

<Switch/> renders the first matching location specified by its <Route/> children. When nothing is matched, the last <Route/> is returned — usually as a 404 page.

render() {
  return (
    <div className="app">
      <div className="menu">
        <Link className="link" to='/'>Home</Link>
        <Link className="link" to='/about'>About</Link>
        <Link className="link" to='/contact'>Contact</Link>
      </div>
      <Switch>
        <Route exact path="/" component={Home} />
        <Route exact path="/contact/" component={Contact} />
        <Route exact path="/about" component={About} />
        <Route>{'404'}</Route>
      </Switch>
    </div>
  );
}

Creating a modal component

So far, we’ve implemented the basic routing structure. Now we can create a modal component and work on displaying it as an overlay.

Although there are a variety of different methods for creating modal components, we’ll only be covering one.

A modal component has a wrapper element which spans the whole screen — width and height.

This area also acts as a clickedOutside detector. Then the actual modal element is positioned relative to that wrapper element.

Below is an example of a <Modal/> functional component using withRouter HOC (Higher order component) to access the router history and call the goBack() method to change the application URL when the modal is closed on click to .modal-wrapper.

onClick={e => e.stopPropagation()} is used to prevent propagation of the click event and trigger the onClick on .modal-wrapper, which would close the modal when the actual .modal element is activated.

import React from 'react';
import { withRouter } from 'react-router-dom';

const Modal = () => (
  <div
    role="button"
    className="modal-wrapper"
    onClick={() => this.props.history.goBack()}
  >
    <div
      role="button"
      className="modal"
      onClick={e => e.stopPropagation()}
    >
      <p>
        CONTENT
      </p>
    </div>
  </div>
);

export default withRouter(Modal);

Styling the .modal-wrapper is just as important. Below, you can find the basic styling used to make it span the whole screen and appear above the content.

Using -webkit-overflow-scrolling: touch enables elastic scroll on iOS devices.

.modal-wrapper {
  position: fixed;
  left: 0;
  top: 0;
  width: 100%;
  height: 100vh;
  overflow-y: scroll;
  -webkit-overflow-scrolling: touch;
}

Opening the modal view

The modal component we created should render on top of the existing view when a specific URL is matched, meaning that somehow we have to change the URL so the routing mechanism can decide what to render.

We know that <Switch/> renders the first matching location, but a modal overlay needs two <Route/> components rendering at the same time.

This can be achieved by putting the modal <Route/> out of <Switch/> and rendering it conditionally.

In this case, we should be able to detect if a modal is active or not.

The easiest way to do this is by passing a state variable along with a <Link/> component.

In the same way we used the <Link/> component to create the navigation menu, we’ll use it to trigger a modal view.

The usage shown below lets us define a state variable, which is then made available in the location prop, which we can access within any component using withRouter HOC.

<Link
  to={{
    pathname: '/modal/1',
    state: { modal: true }
  }}
>
  Open Modal
</Link>

Put this anywhere you want. Clicking the link will change the URL to /modal/1.

There might be several modals with different names like modal/1, modal/2, and so on.

In this case, you’re not expected to define each <Route/> intended to match the individual modal locations. In order to handle all of them under the /modal route, use the following syntax:

<Route exact path="/modal/:id">

This gives you the flexibility of getting the value of the hardcoded :id parameter within the modal component through the match.params prop.

It also lets you do dynamic content renderings, depending on which modal is open.

Matching the modal location

This section is particularly important because it identifies the mechanism for displaying a modal on top of an existing view even though the location parameter changes when a modal is opened.

When we click the Open Modal link defined in the previous section, it will change the location path to /modal/1, which matches nothing in <Switch/>.

So we have to define the following <Route/> somewhere.

<Route exact path="/modal/:id" component={Modal} />

We want to display the <Modal/> component as an overlay.

However, putting it inside <Switch/> would match it and only render the <Modal/> component. As a result, there would be no overlay.

To resolve this problem, we need to define it both inside and outside of <Switch/> with extra conditions.


Below, you’ll see the modified version of the same snippet. There are several changes. Let’s list them quickly:

  • There is a previousLocation variable defined in the constructor.

  • There is an isModal variable defined, which depends on some other values.

  • <Switch/> is using a _location_ prop.

  • There are two <Route exact_path_="/modal/:id" component={Modal} /> used both inside and outside <Switch/>, and the one outside is conditionally rendered.

When a modal is opened, we need to store the previous location object and pass this to <Switch/> instead of letting it use the current location object by default.

This basically tricks <Switch/> into thinking it’s still on the previous location — for example / — even though the location changes to /modal/1 after the modal is opened.

This can be achieved by setting the location prop on <Switch/>.

The following snippet replaces the previousLocation with the current location object when there is no open modal.

When you open a modal, it doesn’t modify the previousLocation.

As a result, we can pass it to <Switch/> to make it think we’re still on the same location, even though we changed the location by opening a modal.

We know that when a modal is opened, the state variable named modal in the location object will be set to true.

We can check if the state of the location object is defined and has the state variable of modal set to true.

However, these two checks alone do not suffice in the case of refreshing the page.

While the modal has to be closed on its own, location.state && location.state.modal still holds.

Checking whether this.previousLocation !== location, we can make sure that refreshing the page will not result in setting isModal to true.

When the modal route is visited directly, which is modal/1 in our example, then none of the checks are true.

Now we can use this boolean value to both render the <Route/> outside of the <Switch/>, and to decide which location object to pass to location prop of <Switch/>.

Given that <Modal/> component has the necessary stylings, this results in two different views rendering on top of each other.

constructor(props){
  super(props);
  this.previousLocation = this.props.location;
}

componentWillUpdate() {
  const { location } = this.props;
  if (!(location.state && location.state.modal)) {
    this.previousLocation = this.props.location;
  }
}  

render() {
  const { location } = this.props;
  const isModal = (
    location.state &&
    location.state.modal &&
    this.previousLocation !== location
  );

  return (
    <div className="app">
      <div className="menu">
        <Link className="link" to='/'>Home</Link>
        <Link className="link" to='/about'>About</Link>
        <Link className="link" to='/contact'>Contact</Link>
      </div>
      <Switch location={isModal ? this.previousLocation : location}>
        <Route exact path="/" component={Home} />
        <Route exact path="/contact/" component={Contact} />
        <Route exact path="/about" component={About} />
        <Route exact path="/modal/:id" component={Modal} />
        <Route>{'no match'}</Route>
      </Switch>
      {isModal
        ? <Route exact path="/modal/:id" component={Modal} />
        : null
      }
    </div>
  );
}

Rendering different modal views

So far we have implemented our modal in a way that ensures we don’t render an overlay when refreshing a page with an open modal, or when directly visiting a modal route.

Instead, we only render the matching <Route/> inside <Switch/>.

In this case, the styling you want to apply is likely to be different, or you might want to show a different content.

This is pretty easy to achieve by passing the isModal variable as a prop on the <Modal/> component, as shown below.

Then, depending on the value of the prop, you can apply different stylings or return a completely different markup.

return (
  <div className="app">
    <div className="menu">
      <Link className="link" to='/'>Home</Link>
      <Link className="link" to='/about'>About</Link>
      <Link className="link" to='/contact'>Contact</Link>
    </div>
    <Switch location={isModal ? this.previousLocation : location}>
      <Route exact path="/" component={Home} />
      <Route exact path="/contact/" component={Contact} />
      <Route exact path="/about" component={About} />
      <Route exact path="/modal/:id" component={Modal} />
      <Route>{'no match'}</Route>
    </Switch>
    {isModal
      ? <Route exact path="/modal/:id">
          <Modal isModal />
        </Route>
      : null
    }
  </div>
);

Preventing the scroll underneath the modal

When you open the modal on some browsers it may have the content below scrolling underneath the modal, which is not a desirable interaction.

Using overflow: hidden on body is the first attempt to block scrolling on the entire page.

However, although this method works fine on desktop, it fails on mobile Safari since it basically ignores overflow: hidden on body.

There are several different npm packages attempting to remedy this scroll locking issue virtually across all platforms.

I found the body-scroll-lock package quite useful.

From this package, you can import disableBodyScroll and enableBodyScroll functions, which accept a reference to the element for which you want scrolling to persist as an input.

When the modal is open we want to disable scrolling for the entire page, except for the modal itself.

Therefore, we need to call disableBodyScroll and enableBodyScroll functions when the modal component is mounted and unmounted, respectively.

To get a reference to the parent <div> of the modal component, we can use the createRef API from React and pass it as a ref to the parent <div>.

The code snippet below disables scrolling when the modal is open and enables it again when the modal component is about to be unmounted.

Using this.modalRef as the input for these imported functions prevents the content of the modal component from being scroll-locked.

Before using the disableBodyScroll function, we need a simple check.

This is because a modal component might get mounted if the page is refreshed when a modal is open, or when the modal route is visited directly.

In both cases, scrolling should not be disabled.

We have already passed the isModal variable as a prop to the <Modal/> component to render different views, so we can just use this prop to check if there is actually a modal.

Below is the modified version of the modal component:

import { disableBodyScroll, enableBodyScroll } from 'body-scroll-lock';

class Modal extends Component {
  constructor(props) {
    super(props);
    this.modalRef = React.createRef();
  }
  
  componentDidMount() {
    const { isModal } = this.props;

    if (isModal) {
      disableBodyScroll(this.modalRef.current);
    }
  }
  
  componentWillUnmount() {
    enableBodyScroll(this.modalRef.current);
  }
  
  render() {
    return (
      <div
        ref={this.modalRef}
        className="modal-wrapper"
        onClick={() => this.props.history.goBack()}
      >
        <div
          className="modal"
          onClick={e => e.stopPropagation()}
        >
        </div>
      </div>
    )
  }
}

Conclusion

You now have an understanding of how a modal view works, as well as a sense of some of the problems you may encounter while implementing your own integration.

For the fully functional example, visit this code sandbox project.

React Router Tutorial: Learn the basics of routing in React

React Router Tutorial: Learn the basics of routing in React

In this React Router tutorial, we're going to learn the basics of routing in React by building navigation for a Scrimba knitting shop website.

Sometimes you've only got 5 minutes to spare. Instead of wasting it on social media, let's get a 5-minute introduction to React-Router! In this tutorial, we're going to learn the basics of routing in React by building navigation for a Scrimba knitting shop website. It's not real, but maybe one day... ;)

What is React-Router, anyway?

Many modern websites are actually made up of a single page, they just look like multiple pages because they contain components which render like separate pages. These are usually referred to as SPAs - single-page applications. At its core, what React Router does is conditionally render certain components to display depending on the route being used in the URL (/ for the home page, /about for the about page, etc.).

For example, we can use React Router to connect www.knit-with-scrimba.com/ to www.knit-with-scrimba.com/about or www.knit-with-scrimba.com/shop

Sounds great - how do I use it?

To use React Router, you first have to install it using NPM:

npm install react-router-dom

Alternatively, you can just use this playground in Scrimba, which has the completed code already written.

You'll need to import BrowserRouter, Route, and Switch from react-router-dom package:

import React, { Component } from 'react';
import { BrowserRouter, Route, Switch } from 'react-router-dom';

In my example, I link the landing page with two other "pages" (which are actually just components) called Shop and About.

First, you'll need to set up your app to work with React Router. Everything that gets rendered will need to go inside the <BrowserRouter> element, so wrap your App in those first. It's the component that does all the logic of displaying various components that you provide it with.

// index.js
ReactDOM.render(
    <BrowserRouter>
        <App />
    </BrowserRouter>, 
    document.getElementById('root')
)

Next, in your App component, add the Switch element (open and closing tags). These ensure that only one component is rendered at a time. If we don't use this, we can default to the Error component, which we're going to write later.

function App() {
    return (
        <main>
            <Switch>

            </Switch>
        </main>
    )
}

It's now time to add your <Route> tags. These are the links between the components and should be placed inside the <Switch> tags.

To tell the <Route> tags which component to load, simply add a path attribute and the name of the component you want to load with component attribute.

<Route path='/' component={Home} />

Many homepage URLs are the site name followed by "/", for example, www.knit-with-scrimba.com/. In this case, we add exact to the Route tag. This is because the other URLs also contain "/", so if we don't tell the app that it needs to look for just /, it loads the first one to match the route, and we get a pretty tricky bug to deal with.

function App() {
    return (
        <main>
            <Switch>
                <Route path="/" component={Home} exact />
                <Route path="/about" component={About} />
                <Route path="/shop" component={Shop} />
            </Switch>
        </main>
    )
}

Now import the components into the app. You may wish to have them in a separate "components" folder to keep code clean and readable.

import Home from './components/Home';
import About from './components/About';
import Shop from './components/Shop';

Onto that error message I mentioned earlier, which loads if a user types an incorrect URL. This is just like a normal <Route> tag, but with no path. The Error component contains <h1>Oops! Page not found!</h1>. Don't forget to import it into the app.

function App() {
    return (
        <main>
            <Switch>
                <Route path="/" component={Home} exact />
                <Route path="/about" component={About} />
                <Route path="/shop" component={Shop} />
                <Route component={Error} />
            </Switch>
        </main>
    )
}

So far, our site is only navigable by typing the URLs. To add clickable links to the site, we use the Link element from React Router and set up a new Navbar component. Once again, don't forget to import the new component into the app.

Now add a Link for each component in the app and use to="URL" to link them.

function Navbar() {
  return (
    <div>
      <Link to="/">Home </Link>
      <Link to="/about">About Us </Link>
      <Link to="/shop">Shop Now </Link>
    </div>
  );
};

Your site now has clickable links that can navigate you around your single-page app!

Conclusion

So there we have it. If you want to easily navigate around a React app, forget the anchor tags and add React Router. It's clean, it's organized, and it makes adding and deleting pages a whole lot easier.

Happy coding ;)

Bonus: Learn React Router in 10 Minutes! | React Tutorials

How to Share Code Between React and React Native

How to Share Code Between React and React Native

Learn how to share code between React and React Native to avoid duplicating logic. Sharing Code Between React and React-Native: What Not to Share. This question of sharing code between React and React Native, in fact, one of the hot topics among React and React native developers all over the world. React and React-Native allow a learn once write anywhere paradigm. This is great, because one tech team can build both your web app and native mobile experience. The problem is developers hate writing things twice. There have been a couple of efforts to build a unifying technology to write an application once and have it work on both web and native.

How to Share Code Between React and React Native

Sharing Code Between React and React-Native: What Not to Share - Ben Ellerby

React and React-Native allow a learn once write anywhere paradigm. This is great, because one tech team can build both your web app and native mobile experience. The problem is developers hate writing things twice. There have been a couple of efforts to build a unifying technology to write an application once and have it work on both web and native. Yet this is not always the best approach. There is value in only sharing your business and state logic; keeping your render code separate.

In this talk I will give real examples from my work with MADE.COM, migrating their web and mobile application to React and React-Native with code sharing as a primary objective.

How to Share Code Between React and React Native

Learn how to share code between React and React Native to avoid duplicating logic

Sharing Code Between React and React Native

React and React-Native allow a learn once write anywhere paradigm. This is great, because one tech team can build both your web app and native mobile experience. The problem is developers hate writing things twice. There have been a couple of efforts to build a unifying technology to write an application once and have it work on both web and native. Yet this is not always the best approach. There is value in only sharing your business and state logic; keeping your render code separate.

Top 8 React Upload Component for Your App

Top 8 React Upload Component for Your App

React Upload: proper and easy way!

The React Upload is a component for uploading one or multiple files, images, documents, audio, video, and other files to a server. It is an extended version of the HTML5 upload component () with a rich set of features that includes multiple file selection, progress bars, auto-uploading, drag and drop, folder (directory) uploading, file validation, and more.

1. react-file-upload-mobile

React Mobile Single File Upload Component.

Download: https://github.com/chengsmart/react-file-upload-mobile/archive/master.zip


2. react-butterfiles

A small component for building file upload fields of any type, for example a simple file upload button or an image gallery field with drag and drop support and preview of selected images.

Demo: https://react-butterfiles.netlify.com/

Download: https://github.com/doitadrian/react-butterfiles/archive/master.zip


3. react-firebase-file-uploader

A file uploader for react that uploads images, videos and other files to your firebase storage.

Demo: https://npm.im/react-firebase-file-uploader

Download: https://github.com/fris-fruitig/react-firebase-file-uploader/archive/master.zip


4. react-avatar

Load, crop and preview avatar with ReactJS component.

Demo: https://kirill3333.github.io/react-avatar/

Download: https://github.com/kirill3333/react-avatar/archive/master.zip


5. Filestack React

This is the official React component for Filestack API and content management system that makes it easy to add powerful file uploading and transformation capabilities to any web or mobile application.

Demo: https://www.filestack.com/

Download: https://github.com/filestack/filestack-react/archive/master.zip


6. itsa-react-fileuploadbutton

React-component: file-uploader, but exposed as a simple button without input-area and with extended features

Download: https://github.com/ItsAsbreuk/itsa-react-fileuploadbutton/archive/master.zip


7. React Images Uploader

React.js component for uploading images to the server.

Download: https://github.com/aleksei0807/react-images-uploader/archive/master.zip


8. React Image Preview and Upload

A simple react component to handle uploading previewing an image before uploading it.

Demo & Source Code: https://codepen.io/hartzis/pen/VvNGZP