Reputation: 199
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
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
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
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
Reputation: 234
You have to do it in react way. You can achieve this by creating a Higher Order Component.
Check this out. https://stackblitz.com/edit/snackbar-hoc?file=src/SnackbarHOC.js
Upvotes: 21