如何实现 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');
你必须以反应的方式来做。您可以通过创建高阶组件来实现。
- 创建一个 HOC,其中 returns 一个 snackbar 组件和 wrappedComponent
- 在 HOC 中创建一个接受消息、严重性的函数(如果您使用 Alert like me), duration and sets the appropriate states which are set to the props of the snackbar。并将该函数作为 prop 传递给 wrappedComponent。
- 最后在任何你想显示小吃店的地方导入这个 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>
)
}
我正在使用 material-ui Snackbar 创建我的 React 应用程序。
在我的项目中,我有很多组件,不想在每个组件中插入
类似于:
import showSnackbar from 'SnackbarUtils';
showSnackbar('Success message');
你必须以反应的方式来做。您可以通过创建高阶组件来实现。
- 创建一个 HOC,其中 returns 一个 snackbar 组件和 wrappedComponent
- 在 HOC 中创建一个接受消息、严重性的函数(如果您使用 Alert like me), duration and sets the appropriate states which are set to the props of the snackbar。并将该函数作为 prop 传递给 wrappedComponent。
- 最后在任何你想显示小吃店的地方导入这个 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>
)
}