How to Create Custom Flash Messages in React [Part 1]

Written by uryelah | Published 2020/05/30
Tech Story Tags: react | material-design | web-development | spa | beginners | tutorial | fun | frontend

TLDR How to Create Custom Flash Messages in React [Part 1] The first part of this tutorial we are going to build a flash message step-by-step with React and Material UI. We want to make the flash show up only when we submit an email from the form though, so we'll need to add some state to this component. We need to import the useState hook form React and set a flash state in the first line of the WithFlash component. You can also add a little conditional submission of the form to hide the flash message.via the TL;DR App

In the first part of this tutorial we are going to build a flash message step-by-step with React and Material UI.
So you are coming from working with rails and bootstrap, you are now building something with React and though the learning curve is steep enough things are somewhat working like they should.
You just need to add a simple flash for confirmation when a user fills in a form, easy enough in rails, but you are now in the Virtual DOM land, house of hooks and states and 'this bug was not here when I tested it yesterday'... so, where do you even start from?
Well fear not my friend, pretend I'm the door Jack gave to Rose so that she would not die in the freezing waters after Titanic sank while he sunk deep into the ocean with her heart, hold tight.
First we'll need to install Material UI, MUI for short, we don't want to style the flash messages from zero and this library has some neat components for our little project:
npm install @material-ui/core @material-ui/lab
Cool, now let's import MUI Alert and add it into a simple component together with a form:

import React, { useEffect, useState } from 'react';
import Alert from '@material-ui/lab/Alert';

const styles = {
  alert: {
    left: '0',
    pointerEvents: 'none',
    position: 'fixed',
    top: 0,
    width: '100%',
    zIndex: '1500',
  },
  form: {
    alignItems: 'center', 
    backgroundColor: '#fff', 
    borderRadius: '5px', 
    boxShadow: '0 10px 15px 0 #ccc', 
    display: 'flex', 
    flexDirection: 'column', 
    justifyContent: 'space-around', 
    width: '400px', 
    height: '300px', 
    margin: '0 auto', 
    padding: '50px 0',
  },
  input: {
    width: '200px',
  },
  main: {
    backgroundColor: '#eef',
    height: '100vh',
    padding: '80px 20px',
  },
  text: { 
    fontSize: '1.2rem', 
    fontWeight: '700', 
    marginBottom: '20px',
  },
};

const WithFlash = () => {
  const handleSubmit = e => {
    e.preventDefault();
  };

  return (
    <div>
      <Alert style={styles.alert} severity="success">This is a success message! It could be an info, warning, or error too!</Alert>
      <div style={styles.main}>
        <form style={styles.form} onSubmit={handleSubmit}>
          <p style={styles.text}>Enter the correct email</p>
          <input style={styles.input} id="email" type="text" />
          <input style={styles.input} type="submit" value="Submit" />
        </form>
      </div>
    </div>
  )
};

export default WithFlash;
You ignore the very long styles object and your eyes go directly to the WithFlash component. You notice how the Alert component takes a severity prop and think it's quite similar to Bootstrap after all. You try the code out in your own React project and you see a form with a permanent flash fixed at the top of the page.
Isn't that nice? Everything cute and static, nothings works, but nothings breaks either.
We want to make the flash show up only when we submit an email from the form though, so we'll need to add some state to this component.
import React, { useState } from 'react';

...
const WithFlash = () => {
  const [flash, setFlash] = useState(null);
....
You import the useState hook form React and set a flash state in the first line of the WithFlash component.
You infer that the thing we want to hide or show is the Flash message, and the action that will trigger that change is the submission of the form, so you add a little conditional to the Alert component and sets the flash state at the handleSubmit function:
...
const WithFlash = () => {
  const [flash, setFlash] = useState(null);

  const handleSubmit = e => {
    e.preventDefault();

    setFlash(true);
  };

  return (
    <div>
      {
        flash
        ? <Alert style={styles.alert} severity="success">This is a success message! It could be an info, warning, or error too!</Alert>
        : null
      }
...
You add the above lines to your code and test the component again, now the Alert is initially not visible, but as soon as you click 'Submit' the message shows up and... it just sits there forever afterwords.
Now, I know, the flash should go away after a couple of seconds, after the user had just about enough time to read it and get its meaning.
This can be simply solved with some good ol' Vanilla JavaScript. So you google really quickly if it was setInterval or setTimeout or something and add a setTimeout function that will change the state of flash back to null after 5 seconds.
  const handleSubmit = e => {
    e.preventDefault();

    setFlash(true);

    setTimeout(() => {
      setFlash(null);
    }, 5000);
  };
How cute!
You test your component again and the after clicking the 'Submit' button the message shows up and crawls back into the void of nothingness after 5 seconds, you give yourself a little pat on the back.
The Flash message transitions are quite abrupt though, and that's where we can use more Material UI goodness!
Meet the Fade component, your new best friend!
You have no idea how to use Fade, or maybe you do but still decide to
entertain my explanation, so you sit back and take note on how to import
and add this little fella to our little project:
import Fade from '@material-ui/core/Fade';

  ...

  return (
    <div>
      {
        flash
        ? (
          <Fade in={flash} timeout={{ enter: 300, exit: 1000 }}>
            <Alert style={styles.alert} severity="success">This is a success message! It could be an info, warning, or error too!</Alert>
          </Fade>
        )
        : null
      }
You notice how Fade takes in a prop, what will prompt its transitions, and a timeout prop containing an object with the enter and exit properties, those are the milliseconds the Fade takes to transition in and out.
Seeing how things are looking quite sensible you add the new code and test the component again, and... there it is! What a nice fade-in transition! The fade-out one though, you can't see it. The Alert is still disappearing abruptly like before.
You take some time to re-read the code you just copied and pasted, because who the heck has the time to type in this economy!?, and find an issue with the flash conditional. The moment the flash state goes back to null, after exact 5 seconds the Fade component is unmounted and not there anymore to realise its glorious transitions.
You tweak the code a bit to fix that issue and...
...
  return (
    <div>
      {
        <Fade in={flash} timeout={{ enter: 300, exit: 1000 }}>
          <Alert style={styles.alert} severity="success">This is a success message! It could be an info, warning, or error too!</Alert>
        </Fade>

      }
...
Ahh, that was a very satisfying fade-out. Good stuff.
You're ready pack your bag an leave this tab when you notice that we've been showing the same message all the time and that's a big nono since you'll need customized messages for the Alerts in your app.
You decide that now the flash state will take an object, with a message and severity to pass to the Alert. So modify the initial flash state and make a little helper function to set the flash object based on the email input by the user. You also create a new state, showFlash, that will only handle just trigger the Fade transitions, like the alert state did before.
...

const checkEmail = (email) => {
  const flash = {}

  if (email === '') {
    flash.message = 'Please input an email';
    flash.severity = 'warning';
  } else if (email === 'rumplestiltskin@mail.com') {
    flash.message = 'Thats the correct email!';
    flash.severity = 'success';
  } else {
    flash.message = 'BEEEP! Wrong email.';
    flash.severity = 'error';
  }

  return flash;
};

const WithFlash = () => {
  const [flash, setFlash] = useState({
    severity: '',
    message: '',
  });
  const [showFlash, setShowFlash] = useState(null);

  const handleSubmit = e => {
    e.preventDefault();

    setShowFlash(true);
    
    const email = e.target[0].value;

    setFlash(checkEmail(email));

    setTimeout(() => {
      setShowFlash(false);
    }, 5000);
  };

  return (
    <div>
      <Fade in={showFlash} timeout={{ enter: 300, exit: 1000 }}>
        <Alert style={styles.alert} severity={flash.severity}>{flash.message}</Alert>
      </Fade>
...
That's quite the piece of chunky code! The checkEmail function returns different messages and severity for the flash state, the handle submit gained a few more lines also changing the showFlash state and getting the email for the checkEmail function and the Fade component changed it's in prop from flash to showFlash.
You test your code a bit, submitting it with an empty input, again with a random email and once more with the correct email set in checkEmail.
The style and content of the Flash message changes with each case. You call this a success and a little real world Flash message pops out of the screen and over your machine congratulating you as you worry that maybe what granny had could have been passed down to you and if you should call the doctor after all but you tell yourself it will be all just fine as long as you ignore the message lingering over the notebook and whisper to yourself "it's not real it's not real it's not real".
You then stop and consider, what if you want a Flash to show up after an user logs in? How can you make it show up in another page or another component? You feel nauseous just from thinking about all the common parent components you'll need and all the props you'll need to drill.
But fear not! At PART 2 of this tutorial we'll explore how to make Flashes show up after a page redirection, and I bet it will be simpler than you think.

Written by uryelah | @uryelah on github and twitter
Published by HackerNoon on 2020/05/30