Global Snackbars in React with Redux, and Material UI

📅 September 12, 2019

👷 Chris Power

I’m working on a side project in React with Redux and Material UI. I love how snackbars look, and I wanted to create an easy system for displaying them throughout my application. If you’ve ever worked in Rails, picture the way you display flash messages. I wanted something simple like that amazing Rails feature.

About Snackbars

From the Material Design Guidelines: “Snackbars provide brief messages about app processes at the bottom of the screen”.

The ☝ ️guidelines also specify the way you’re supposed to implement snackbars: “Snackbars inform users of a process that an app has performed or will perform. They appear temporarily, towards the bottom of the screen. They shouldn’t interrupt the user experience, and they don’t require user input to disappear”. This is the perfect use case to show a user when they completed a successful action on your application!

Snackbars in Material UI

Material UI is a component framework for React based on Material IO guidelines. They have an excellent snackbar component that we can leverage to show snackbar notifications to our users.

The problem in my application

I wanted to use snackbars all over my application. There are many places where a user can take an action that results in a snackbar appearing. I didn’t want to instantiate a new Snackbar component for every single component that requires one to be displayed. Here is a quick example of one snackbar in my app:



snackbar running in the app

Glorious snackbars, so yummy


The Solution: Redux!

Thankfully I am using React, with Redux. And I love using Redux as often as possible to solve my state issues. There is a pattern that I love with redux that I like to call the “redux ui” pattern. Basically I use redux to store at least some of my UI state throughout the app. This makes global UI changes (think users specifying dark mode in their apps) easy to store and propagate throughout your app.

  • Lets start by creating our reducers, this will give us the shape of our state for displaying snackbars:
// reducers/uiReducer.js
const uiReducer = (state = {}, action) => {
  switch (action.type) {
    case "SNACKBAR_SUCCESS":
      return {
        ...state,
        successSnackbarOpen: true,
        successSnackbarMessage: action.message
      };
    case "SNACKBAR_CLEAR":
      return {
        ...state,
        successSnackbarOpen: false,
        errorSnackbarOpen: false,
        infoSnackbarOpen: false
      };
    default:
      return state;
  }
};

export default uiReducer;

☝️ Will give us some structure for displaying a “success” snackbar, along with clearing out all possible snackbars we may want to create (including error and info… just in case).

  • Lets make some action creators to run the reducers.
// actions/snackbarActions.js
export const showSuccessSnackbar = message => {
  return dispatch => {
    dispatch({ type: "SNACKBAR_SUCCESS", message });
  };
};

export const clearSnackbar = () => {
  return dispatch => {
    dispatch({ type: "SNACKBAR_CLEAR" });
  };
};
  • Create a snackbar that uses the clearSnackbar() function to clear itself
// components/SuccessSnackbar.js or whatever you wanna call it
import { useDispatch, useSelector } from "react-redux";
import Snackbar from "@material-ui/core/Snackbar";
import IconButton from "@material-ui/core/IconButton";
import { Icon } from "@material-ui/core";
import { clearSnackbar } from "../../store/actions/snackbarActions";

export default function SuccessSnackbar() {
  const dispatch = useDispatch();

  const { successSnackbarMessage, successSnackbarOpen } = useSelector(
    state => state.ui
  );

  function handleClose() {
    dispatch(clearSnackbar());
  }

  return (
    <Snackbar
      anchorOrigin={{
        vertical: "bottom",
        horizontal: "left"
      }}
      open={successSnackbarOpen}
      autoHideDuration={4000}
      onClose={handleClose}
      aria-describedby="client-snackbar"
      message={
        <span id="client-snackbar">
          <Icon>check_circle</Icon>
          {successSnackbarMessage}
        </span>
      }
      action={[
        <IconButton
          key="close"
          aria-label="close"
          color="inherit"
          onClick={handleClose}
        >
          <Icon>close</Icon>
        </IconButton>
      ]}
    />
  );
}

in ☝️ note that we are using the clearSnackbar() function when the snackbar calls handleClose. Note, handleClose is called after the timeout specified, so the snackbar will automatically call this function after a certain amount of time.

  • Add this new snackbar component to your App.js file. This allows you to display a snackbar ANYWHERE in your app using redux:
return (
  <div>
    <SuccessSnackbar />
    <Router>
      //App stuff goes in here
    </Router>
  </div>
);
  • Dispatch the showSuccessSnackbar() function with your message whenever you want to show a success snackbar.
dispatch(showSuccessSnackbar("Success!"));

Conclusion

Success! Now you can display a snackbar anywhere in your React app using Redux. You are such a smart developer, aren’t you? 😄

I found this to be a fun and interesting pattern to use when you want to globally display snackbars, or alerts, or anything in your React application. It’s straightforward enough for any developer to understand, and its extensible to add anything: alerts, snackbars, messages, etc…

Lets Work Together

We're trusted by large, medium, and small companies all over the world

Have something you're working on?

Tell Us About It