Как реализовать material-ui Snackbar как глобальную функцию?

#javascript #reactjs #notifications #material-ui #snackbar

#javascript #reactjs #уведомления #material-ui #snackbar

Вопрос:

Я создаю свое приложение react с помощью material-ui Snackbar. В моем проекте у меня много компонентов, и я не хочу вставлять <Snackbar/> в каждый из них. Есть ли способ создать функцию, которая будет показывать snackbar, а затем просто импортировать и использовать эту функцию в каждом компоненте?

Что-то вроде:

импортируйте showSnackbar из ‘SnackbarUtils’;

Показывает snackbar(‘Сообщение об успешном завершении’);

Ответ №1:

Вы должны сделать это в react way. Вы можете достичь этого, создав компонент более высокого порядка.

  1. Создайте HOC, который возвращает компонент snackbar вместе с WrappedComponent
  2. Создайте функцию в этом HOC, которая принимает сообщение, серьезность (если вы используете предупреждение, как я), продолжительность и устанавливает соответствующие состояния, которые устанавливаются в реквизит snackbar. И передайте эту функцию в качестве поддержки WrappedComponent .
  3. Наконец, импортируйте этот 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>
  )
}