Luciferous
Luciferous

Reputation: 199

How to implement material-ui Snackbar as a global function?

I am creating my react app with material-ui Snackbar. In my project I have a lot of components and don't want to insert <Snackbar/> in each of them. Is there a way to create function that will show snackbar, then just import and use this function in each component?

Something like:

import showSnackbar from 'SnackbarUtils';

showSnackbar('Success message');

Upvotes: 18

Views: 21389

Answers (4)

nivlac
nivlac

Reputation: 390

I'd recommend using notistack - it takes care of the state for you and can be used across components with a single instantiation: https://github.com/iamhosseindhv/notistack.

It's also mentioned in the MUI docs: https://mui.com/material-ui/react-snackbar/#notistack.

Upvotes: 0

Ilmari Kumpula
Ilmari Kumpula

Reputation: 1465

Here is a sample code for fully working example using Redux, Material-ui and MUI Snackbar

import { random } from 'lodash'
import { Action } from 'redux'
import actionCreatorFactory, { isType } from 'typescript-fsa'

const actionCreator = actionCreatorFactory()

export type Notification = {
  message: string
}

export type NotificationStore = Notification & {
  messageId: number
}

export const sendNewNotification =
  actionCreator<Notification>('NEW_NOTIFICATION')

const defaultState: NotificationStore = { message: '', messageId: 1 }

const reducer = (
  state: NotificationStore = defaultState,
  action: Action
): NotificationStore => {
  if (isType(action, sendNewNotification)) {
    const {
      payload: { message }
    } = action
    return { message, messageId: random(0, 200000) }
  }

  return state
}

export default reducer





// useNotification to get state from Redux, you can include them into same file if you prefer

import { NotificationStore } from './notification'

export function useNotification(): NotificationStore {
  return useSelector<NotificationStore>(
    (state) => state.notification
  )
}


// Notification React-component - Notification.tsx

import React, { useState } from 'react'

import Button from '@mui/material/Button'
import Snackbar from '@mui/material/Snackbar'
import IconButton from '@mui/material/IconButton'
import CloseIcon from '@mui/icons-material/Close'

type Props = {
  message: string
}

export function Notification({ message }: Props): JSX.Element | null {
  const [notiOpen, setNotiOpen] = useState(true)

  if (!message) {
    return null
  }

  return (
    <Snackbar
      anchorOrigin={{
        vertical: 'bottom',
        horizontal: 'left'
      }}
      open={notiOpen}
      autoHideDuration={10000}
      onClose={() => setNotiOpen(false)}
      message={message}
      action={
        <React.Fragment>
          <Button
            color="secondary"
            size="small"
            onClick={() => setNotiOpen(false)}
          >
            Close
          </Button>
          <IconButton
            size="small"
            aria-label="close"
            color="inherit"
            onClick={() => setNotiOpen(false)}
          >
            <CloseIcon fontSize="small" />
          </IconButton>
        </React.Fragment>
      }
    />
  )
}


// Main App.tsx to run my application

import { Notification } from "./Notification.tsx"
import { useDispatch } from 'react-redux'
import { BrowserRouter as Router, Route, Switch } from 'react-router-dom'

const App: React.FC<AppProps> = () => {
  const dispatch = useDispatch()
  const { message, messageId } = useNotification()

  return (
    <ThemeProvider theme={appTheme}>

      <Router>
        <Switch>
          <Route path="/public/:projectId" component={ProjectPage} />
          <Route path="/login" component={LoginPage} />
          <Route render={() => <PageNotFound />} />
        </Switch>
      </Router>
      <Notification key={messageId} message={message} />
    </ThemeProvider>
  )
}

export default App






// Usage of hook in application - FileSomething.tsx

import { useDispatch } from 'react-redux'
import { useEffect } from 'react'
import { sendNewNotification } from 'src/redux/notification'

export function FileSomething(): JSX.Element {
  
   function sendNotification() {
    dispatch(
      sendNewNotification({
        message: 'Hey, im a notification'
      })
    )
  }

  useEffect(() => {
    sendNotification()
  }, [])

  return (
    <div>Component doing something</div>
  )
}

Upvotes: 1

Bogdan Iordachescu
Bogdan Iordachescu

Reputation: 314

extend it as a Hook, and then you can call it once and use state with effects to show:

import { useSnackbar } from 'notistack';
import IconButton from "@mui/material/IconButton";
import CloseIcon from "@mui/material/SvgIcon/SvgIcon";
import React, {Fragment, useEffect, useState} from "react";


const useNotification = () => {
    const [conf, setConf] = useState({});
    const { enqueueSnackbar, closeSnackbar } = useSnackbar();
    const action = key => (
        <Fragment>
            <IconButton onClick={() => { closeSnackbar(key) }}>
                <CloseIcon />
            </IconButton>
        </Fragment>
    );
    useEffect(()=>{
        if(conf?.msg){
            let variant = 'info';
            if(conf.variant){
                variant = conf.variant;
            }
            enqueueSnackbar(conf.msg, {
                variant: variant,
                autoHideDuration: 5000,
                action
            });
        }
    },[conf]);
    return [conf, setConf];
};

export default useNotification;

Then you can use it:

const [msg, sendNotification] = useNotification();

sendNotification({msg: 'yourmessage', variant: 'error/info.....'})

Upvotes: 18

BeerendraMC
BeerendraMC

Reputation: 234

You have to do it in react way. You can achieve this by creating a Higher Order Component.

  1. Create a HOC that returns a snackbar component along with the wrappedComponent
  2. Create a function in that HOC which accepts message, severity (if you are using Alert like me), duration and sets the appropriate states which are set to the props of the snackbar. And pass that function as a prop to the wrappedComponent.
  3. Finally import this HOC wherever you want to display a snackbar, pass your component in it and call the HOC function from the prop (this.prop.functionName('Hello there!')) in the event handler where you want to display a snackbar and pass in a message.

Check this out. https://stackblitz.com/edit/snackbar-hoc?file=src/SnackbarHOC.js

Upvotes: 21

Related Questions