Muhi Logo Text
AboutBlogWork With Me

How to Create a Built-In Loading Bar for your React Dialog

A step-by-step guide to creating a reusable material form dialog with a loading progress bar

Last updated on June 10, 2021

react
mui
React Dialog

Form dialogs are very common and useful especially when you don’t want to redirect the user to a separate page. A common example would be a newsletter subscription dialog, whether or not users choose to subscribe, they shouldn’t have to navigate back to the initial page.

A good practice is to trigger a loading bar as soon as the user submits a request to the server. Here is an example I created using Material Linear Progress:

But what if we have other form dialogs in a project that requires to behave the same way? In this article, I will go through a step-by-step guide to creating a reusable custom dialog that inherits from React Material and extends functionality to include a built-in loading bar. This way, we have a consistent experience in every dialog without any redundancies.

Setup React Project

Create React App and install React Material

npx create-react-app react-material-dialog
npm install @material-ui/core

Create a Custom Dialog

Let’s create a new Dialog.js component that inherits from Material Dialog and uses a LinearProgress component.

import { LinearProgress } from '@material-ui/core';
import MatDialog from '@material-ui/core/Dialog';

export const Dialog = ({children, ...props}) => {
    return (
        <MatDialog {...props}>
            <LinearProgress></LinearProgress>
            {children}
        </MatDialog>
    )
}

We placed the LinearProgress as the first element within MatDialog followed by the children that will eventually be passed by the consumer. Also, …props should go into MatDialog in order to access all the component’s features (such as open, onClose, aria…).

The reason why I’m importing Material Dialog as MatDialog to not to cause naming conflict with the custom dialog.js we just created

Test Loading Bar

In the main App.js component, let’s try out the custom dialog we just created and check if the loading bar is showing. We will use one of the form dialog examples from React Material but instead of importing the dialog from @material-ui/core will import the custom one ./Dialog

import { useState } from 'react';
import './App.css';
import { Button, DialogTitle, DialogContent, DialogContentText, TextField, DialogActions } from '@material-ui/core';
import { Dialog } from './Dialog'

function App() {
  const [open, setOpen] = useState(false);
  const handleClose = () => {
    setOpen(false);
  };
  return (
    <div className="App">
      <Button onClick={() => setOpen(true)}>Open</Button>
      <Dialog open={open} onClose={handleClose} aria-labelledby="form-dialog-title">
        <DialogTitle id="form-dialog-title">Subscribe</DialogTitle>
        <DialogContent>
          <DialogContentText>
            To subscribe to this website, please enter your email address here. We will send updates
            occasionally.
          </DialogContentText>
          <TextField
            autoFocus
            margin="dense"
            id="name"
            label="Email Address"
            type="email"
            fullWidth
          />
        </DialogContent>
        <DialogActions>
          <Button onClick={handleClose} color="primary">
            Cancel
          </Button>
          <Button onClick={handleClose} color="primary">
            Subscribe
          </Button>
        </DialogActions>
      </Dialog>
    </div>
  );
}

export default App;
@muhimasri
 

React dialog

Create Conditional Loading

As we can see above, the current component is not complete as the loading bar shows all the time.

We can easily adjust this by adding a new loading prop that can be passed by the consumer. It will indicate when to trigger the loading:

import { LinearProgress } from '@material-ui/core';
import MatDialog from '@material-ui/core/Dialog';

export const Dialog = ({loading, children, ...props}) => {
    return (
        <MatDialog {...props}>
            {loading ? <LinearProgress></LinearProgress> : null}
            {children}
        </MatDialog>
    )
}

Now that we have the full component ready and it accepts a loading prop, let’s trigger the loading bar upon submitting the form. We will including a timeout function to mimic an API call and then close the dialog:

import { useState } from 'react';
import './App.css';
import { Button, DialogTitle, DialogContent, DialogContentText, TextField, DialogActions } from '@material-ui/core';
import { Dialog } from './Dialog'

function App() {
  const [open, setOpen] = useState(false);
  const [loading, setLoading] = useState(false);

  const handleClose = () => {
    setOpen(false);
  };

  const handleSubscribe = () => {
    setLoading(true);
    setTimeout(() => {
      setLoading(false);
      setOpen(false);
    }, 2000)
  }
  return (
    <div className="App">
      <Button variant="outlined" onClick={() => setOpen(true)}>Open Dialog</Button>
      <Dialog loading={loading} open={open} onClose={handleClose} aria-labelledby="form-dialog-title">
        <DialogTitle id="form-dialog-title">Subscribe</DialogTitle>
        <DialogContent>
          <DialogContentText>
            To subscribe to this website, please enter your email address here. We will send updates
            occasionally.
          </DialogContentText>
          <TextField
            autoFocus
            margin="dense"
            id="name"
            label="Email Address"
            type="email"
            fullWidth
          />
        </DialogContent>
        <DialogActions>
          <Button onClick={handleClose} color="primary">
            Cancel
          </Button>
          <Button onClick={handleSubscribe} color="primary">
            Subscribe
          </Button>
        </DialogActions>
      </Dialog>
    </div>
  );
}

export default App;

React dialog

And you’re all done! Now, anytime you have a form dialog, all you need to do is to use the new custom dialog and pass it the loading prop.

You can access the complete repository here.

Bye for now 👋

If you enjoyed this post, I regularly share similar content on Twitter. Follow me @muhimasri to stay up to date, or feel free to message me on Twitter if you have any questions or comments. I'm always open to discussing the topics I write about!

Recommended Reading

Learn how to create encapsulated and reusable Fieldset component with Material UI (MUI) and React.

react
mui

Discussion

Upskill Your Frontend Development Techniques 🌟

Subscribe to stay up-to-date and receive quality front-end development tutorials straight to your inbox!

​

No spam, sales, or ads. Unsubscribe anytime you wish.

© 2024, Muhi Masri