#javascript #reactjs #notifications #material-ui #snackbar
#javascript #reactjs #уведомления #material-ui #snackbar
Вопрос:
Я создаю свое приложение react с помощью material-ui Snackbar. В моем проекте у меня много компонентов, и я не хочу вставлять <Snackbar/> в каждый из них. Есть ли способ создать функцию, которая будет показывать snackbar, а затем просто импортировать и использовать эту функцию в каждом компоненте?
Что-то вроде:
импортируйте showSnackbar из ‘SnackbarUtils’;
Показывает snackbar(‘Сообщение об успешном завершении’);
Ответ №1:
Вы должны сделать это в react way. Вы можете достичь этого, создав компонент более высокого порядка.
- Создайте HOC, который возвращает компонент snackbar вместе с WrappedComponent
- Создайте функцию в этом HOC, которая принимает сообщение, серьезность (если вы используете предупреждение, как я), продолжительность и устанавливает соответствующие состояния, которые устанавливаются в реквизит snackbar. И передайте эту функцию в качестве поддержки WrappedComponent .
- Наконец, импортируйте этот HOC везде, где вы хотите отобразить snackbar, передайте в него свой компонент и вызовите функцию HOC из prop (this.prop.functionName(‘Привет!’)) в обработчике событий, где вы хотите отобразить snackbar и передать сообщение.
Проверьте это. https://stackblitz.com/edit/snackbar-hoc?file=src/SnackbarHOC.js
Комментарии:
1. Привет, спасибо, просто запрос, который в вашем
stackblitz
коде просто добавьте"type":"module"
для последней версии node.2. Как вы это используете? Шаг 3 не в примере
Ответ №2:
расширьте его как хук, а затем вы можете вызвать его один раз и использовать состояние с эффектами для отображения:
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;
Затем вы можете использовать его:
const [msg, sendNotification] = useNotification();
sendNotification({msg: 'yourmessage', variant: 'error/info.....'})
Комментарии:
1. Вам не нужно возвращать conf — просто верните setConf
Ответ №3:
Вот пример кода для полностью рабочего примера с использованием Redux, Material-ui и 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 amp; {
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>
)
}