Search code examples
reactjsmaterial-uiapollomutationsnackbar

Show SnackBar Material UI when appear erron in Mutation


I use Snack bar from Materia-UI page (first example - Customized SnackBars)

const variantIcon = {
  success: CheckCircleIcon,
  warning: WarningIcon,
  error: ErrorIcon,
  info: InfoIcon,
};

const styles1 = theme => ({
  success: {
    backgroundColor: green[600],
  },
  error: {
    backgroundColor: theme.palette.error.dark,
  },
  info: {
    backgroundColor: theme.palette.primary.dark,
  },
  warning: {
    backgroundColor: amber[700],
  },
  icon: {
    fontSize: 20,
  },
  iconVariant: {
    opacity: 0.9,
    marginRight: theme.spacing.unit,
  },
  message: {
    display: 'flex',
    alignItems: 'center',
  },
});

function MySnackbarContent(props) {
  const { classes, className, message, onClose, variant, ...other } = props;
  const Icon = variantIcon[variant];

  return (
    <SnackbarContent
      className={classNames(classes[variant], className)}
      aria-describedby="client-snackbar"
      message={
        <span id="client-snackbar" className={classes.message}>
          <Icon className={classNames(classes.icon, classes.iconVariant)} />
          {message}
        </span>
      }
      action={[
        <IconButton
          key="close"
          aria-label="Close"
          color="inherit"
          className={classes.close}
          onClick={onClose}
        >
          <CloseIcon className={classes.icon} />
        </IconButton>,
      ]}
      {...other}
    />
  );
}

MySnackbarContent.propTypes = {
  classes: PropTypes.object.isRequired,
  className: PropTypes.string,
  message: PropTypes.node,
  onClose: PropTypes.func,
  variant: PropTypes.oneOf(['success', 'warning', 'error', 'info']).isRequired,
};

const MySnackbarContentWrapper = withStyles(styles1)(MySnackbarContent);

const styles2 = theme => ({
  margin: {
    margin: theme.spacing.unit,
  },
});

class CustomizedSnackbar extends React.Component {
  state = {
    open: false,
  };

  handleClick = () => {
    this.setState({ open: true });
  };

  handleClose = (event, reason) => {
    if (reason === 'clickaway') {
      return;
    }
    this.setState({ open: false });
  };

  render() {
    return (
      <div>
        <Snackbar
          anchorOrigin={{
            vertical: 'bottom',
            horizontal: 'left',
          }}
          open={this.state.open}
          autoHideDuration={2000}
          onClose={this.handleClose}
        >
          <MySnackbarContentWrapper
            onClose={this.handleClose}
            variant="error"
            message="This is an error message!"
          />
        </Snackbar>
      </div>
    );
  }
}

export default withStyles(styles2)(CustomizedSnackbar);

In the example the snack bar is shown when click on button "OPEN SUCCESS SNACKBAR"

I would like to show the error snack bar when Mutation from Apollo on my Form gives an error.

render(){
 return(
  <div>
   <Mutation
    mutation={this.mutationQuery}
    onError={() =>
     //here show Snack Bar
    }
    onCompleted={data => { console.log(data); }}
   >
  {mutation => (
//here is the form
)}
)}

The problem is I dont know how to trigger to show the SnackBar in the on Error function. How to change state of Snack Bar? I was trying the solution from here, but I receive an error that

openSnackbarFn is not a function

Thanks in advance.


Solution

  • Fundamentally, you want your Snackbar to be a sibling of your Mutation, and let their common parent (i.e. your component) handle the Snackbar open/closed state.

    Class-style component

    class FormWithMutationAndSnackbar extends React.Component {
      state = {
        open: false
      }
    
      handleOpen = () => this.setState({ open: true })
    
      handleClose = () => this.setState({ open: false })
    
      render() {
        const { open } = this.state
        return(
          <React.Fragment>
            <Mutation
              mutation={this.mutationQuery}
              onError={(err) => {
                // use err to set Snackbar contents for example
                this.handleOpen()
              }
              onCompleted={data => { console.log(data); }}
            >
              {mutation => (
                //here is the form
              )}
            </Mutation>
            <Snackbar
              open={open}
              onClose={this.handleClose}
              // other Snackbar props
            >
              // Snackbar contents
            </Snackbar>
          </React.Fragment>
        )
      }
    }
    

    Functional component with Hooks

    const FormWithMutationAndSnackbar = () => {
      const [open, setOpen] = useState(false)
    
      return(
        <React.Fragment>
          <Mutation
            mutation={this.mutationQuery}
            onError={(err) => {
              // use err to set Snackbar contents for example
              setOpen(true)
            }
            onCompleted={data => { console.log(data); }}
          >
            {mutation => (
              //here is the form
            )}
          </Mutation>
          <Snackbar
            open={open}
            onClose={() => setOpen(false)}
            // other Snackbar props
          >
            // Snackbar contents       
          </Snackbar>
        </React.Fragment>
      )
    }