Using a snackbar with react and redux


The Material-UI package has a nice Snackbar component. This is a little like toast in Apps Script - it's a good way to show a transient message. But how to manage the appearance and disappearance of the snackbar in a React Redux environment? In the solution was pretty simple. 

Actions

The idea is that you have 2 actions to dispatch. One that fires to bring up the snackbar, and another to clear the message when it's done with. In this example I want a component that i can re-use on any page.

Here's my actions
        TOAST_DASH_MESSAGE:'TOAST_DASH_MESSAGE',
        TOAST_DASH_CLEAR:'TOAST_DASH_CLEAR',

and my action creators
export function acToastDashMessage (message) {
  
  return {
    type: cs.actions.TOAST_DASH_MESSAGE,
    payload:message
  };

}
export function acToastDashClear () {
  
  return {
    type: cs.actions.TOAST_DASH_CLEAR
  };

}

Reducers

The reducers don't do much. They just set their part of the store to either a message, plus an open state, or a null string and a not open state. This will be used by the snackbar later to decide whether to show itself. By taking care of all this in the redux store, there's no need to manage the state in the component itself. 
import cs from '../constants/params';
const initialState = {
  dash: {
    message:'',
    open:false
  }
};

export default function(state = initialState, action) {

  switch (action.type) {

    case cs.actions.TOAST_DASH_ERROR:
      {
        return {...state,
          dash: {
            message:action.payload,
            open:true
          }
        };
      }

    case cs.actions.TOAST_DASH_CLEAR:
      {
        return {...state,
          dash: {
            message:"",
            open:false
          }
        };
      }

  }

  return state;
}

Component

This is small wrapper for the Snackbar component. It's a dumb component so its expecting the handler to clear out the message once shown to be passed down to it from its parent. 
import React from 'react';
import Snackbar from 'material-ui/Snackbar';

export default class Toaster extends React.Component {

  render() {
    const props = this.props;
    return  (
      <span>
        <Snackbar
          open={props.open}
          message={props.message}
          autoHideDuration={props.duration || 5000}
          onRequestClose={this.props.onRequestClose}
        />
      </span>
    ) ;
  }
}

Requesting a toast

It's just this
store.dispatch(acToastDashMessage("your message"));

Toaster component

The onRequestClose handler simply has to dispatch the clear message action.
  toasted() {
    this.props.dispatch(acToastDashClear()) ;
  }

and the Toaster component is called like this
      <Toaster 
        {...props.toast.dash}
        duration={4000} 
        onRequestClose={this.toasted.bind(this)}
      />

Connecting to the store

Using a decorator is the easiest way. 
@connect(store => {
  return {
    toast: store.toast
  };
})

And that's it. As is the way of react/redux it seems like a lot of steps, but the pattern is very simple and just like all your other react/redux patterns, even though the snackbar component is kind of quirky.






For more like this, see Google Apps Scripts snippets. Why not join our forum, follow the blog or follow me on twitter to ensure you get updates when they are available.
Comments