如何实现 material-ui Snackbar 作为全局函数?

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

我正在使用 material-ui Snackbar 创建我的 React 应用程序。 在我的项目中,我有很多组件,不想在每个组件中插入 。 有没有办法创建显示 snackbar 的函数,然后在每个组件中导入并使用这个函数?

类似于:

import showSnackbar from 'SnackbarUtils';

showSnackbar('Success message');

你必须以反应的方式来做。您可以通过创建高阶组件来实现。

  1. 创建一个 HOC,其中 returns 一个 snackbar 组件和 wrappedComponent
  2. 在 HOC 中创建一个接受消息、严重性的函数(如果您使用 Alert like me), duration and sets the appropriate states which are set to the props of the snackbar。并将该函数作为 prop 传递给 wrappedComponent。
  3. 最后在任何你想显示小吃店的地方导入这个 HOC,在其中传递你的组件并从你所在的事件处理程序中的 prop (this.prop.functionName('Hello there!')) 调用 HOC 函数想显示一个 snackbar 并传递消息。

看看这个。 https://stackblitz.com/edit/snackbar-hoc?file=src/SnackbarHOC.js

扩展成一个Hook,然后调用一次就可以使用state with effects来展示:

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.....'})

这是使用 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 & {
  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>
  )
}