Material UI | How to easily setup popup notifications
4 minutes read
Using Material UI and React Context to set up global notification system.
Need for global notification system
In your application, you want have good user experience and let the user know if the actions/clicks have resulted in success or failure. We should also show warning and information messages to guide user through the flows.
It is of course possible to display a local popup component wherever needed, but it quickly becomes a bloat and irritating to manage. It would be really cool to just set up the whole system once and trigger it from wherever from the app to get consistent experience.
A global system runs like plug-and-play and is isolated from rest of the local logic.
Summary
We will need the following -
- An alert component
- A place to trigger one or more instances and remove ( unmount) them after a set time period
- A way to keep track of notifications list and add new notifications.
Create notifications component
Let’s start with a basic Alert component. It will receive a title and text as props, and also a type of alert as well. We will also create a handleClose function.
We will now need a wrapper component which maps through the list of notifications and feeds each Alert correctly.
const useStyles = makeStyles((theme) => ({
root: {
position: 'fixed',
right: theme.spacing(2),
bottom: theme.spacing(2),
zIndex: 2000, // Make sure it pops up on top of everything
},
alert: {
marginBottom: theme.spacing(2),
},
}))
const AlertProvider = ({ duration = 1000, alert, actions }) => {
const classes = useStyles()
const handleClose = () => {
actions.removeAlert(alert)
}
useEffect(() => {
// run handleClose once duration is finished.
const timer = setTimeout(handleClose, duration)
return function () {
clearTimeout(timer)
}
}, [])
return (
{alert.title}
{alert.text}
)
}
Decide on a global state management system -> Redux or Context
In this article, I will be going with Context and use a global provider to handle state management. We could have used Redux just as well to set it up and keep the rest as it is.
Create Context provider
Let’s set up our global context provider for alerts management
import React, { createContext, useReducer } from 'react'
const AlertContext = createContext([{}, () => {}])
AlertContext.displayName = 'AlertContext'
const initialState = {
alerts: [],
}
let reducer = (state, action) => {
let newAlerts, element
switch (action.type) {
case 'ADD_ALERT':
newAlerts = [...state.alerts, action.payload]
return { ...state, alerts: newAlerts }
case 'REMOVE_ALERT':
element = state.alerts.filter((e) => e.id !== action.payload.id)
return { alerts: element }
default:
console.log('No valid action: ' + action.type)
return state
}
}
function AlertContextProvider(props) {
let [state, dispatch] = useReducer(reducer, initialState)
const actions = {
addAlert: (payload) => {
dispatch({ type: 'ADD_ALERT', payload })
},
removeAlert: (payload) => {
dispatch({ type: 'REMOVE_ALERT', payload })
},
}
return (
{props.children}
)
}
export { AlertContext, AlertContextProvider }
Primarily, the provider handles adding an alert, and removing it.
Wrap app.js in provider
Once we have the basic stuff scaffolded, time to wire them up with rest of the app.
First, make it available for the whole app by adding it on top of the tree.
import React from 'react'
import ReactDOM from 'react-dom'
import { AlertContextProvider } from './AlertProvider'
import App from './App'
const rootElement = document.getElementById('root')
ReactDOM.render(
,
rootElement
)
Set up dispatch on trigger
The very last thing we need to do is to actually trigger the alert somehow.
import React, { useContext } from 'react'
import './styles.css'
import { Button, Card } from '@material-ui/core'
import { AlertContext } from './AlertProvider'
import Notification from './Notification'
export default function App() {
const { actions } = useContext(AlertContext)
const alertTypes = ['success', 'warning', 'info', 'error']
// Select a random type for example app
const selectedType = alertTypes[Math.floor(Math.random() * alertTypes.length)]
const triggerNotification = () => {
actions.addAlert({
text: 'Notification text',
title: ` Clicked on ${selectedType}`,
type: selectedType,
id: Date.now(), // lazy way of adding unique id per alert
})
}
return (
)
}
Codesandbox link for working code
Finally, here is a working example to show how it actually works.