Link:
1.
2.
3.
4.
| Tech | Version |
|--------------|---------|
| Notistack | v1.?.? |
| React | |
| Browser | |
| etc. | |
I need more detail about what you want to achieve before I can help you @nguyenvanphuc2203

i have a function export fetch api, and i want to use notistack replace alert
where do you call the function fetchApi?
(on a side note: token is always undefined in line 19. variable token is only accessible from within the first if statement. to access it outside of if statement do something like the following):
let token = null;
if (...) {
token = //get from localStorage
} else {
// ...
}
// ...
const authorization = token ? {} : {'Authorization': `Bearer ${token}`}
// ...
update: I was not paying attention that token is a var. var is function scoped therefore no problem with your code.
oh! thank for feedback,
i use fetch api function in some component
please provide the code of that component

a example,

butut i want to use notistack inside function common fetch api to show error
enqueueSnackbar can only be accessed via props and props are only available in your component's code. what I recommend is to move the logic (things like checking for status, setting token in localStorage, etc) to fetchApi or another function, and just return promise, resolve on success and reject on failure. That way you can display snackbars and also have a clean, easy-to-follow code. Your result would look something like this:
fetchApi(url, method, payload)
.then(() => {
// ... enqueue snackbar
// ... redirect to /dashboard page
})
.catch((err) => this.props.enqueueSnackbar(err, { variant: 'error' } ))
check this out for more info.
thanks for your help
<3
I have the same use case. It's not possible to tie the notification to any component (it appears in a socket.io event handler). I resolved this by creating a second instance of SnackbarProvider mounted elsewhere in the DOM, as shown below.
<div id='snackbarhelper'></div>
and then this should work:
import React from 'react';
import ReactDOM from 'react-dom';
import { SnackbarProvider, withSnackbar } from 'notistack';
export default {
success: function (msg) {
this.toast(msg, 'success');
},
toast: function (msg, variant) {
const Display = withSnackbar(({ message, enqueueSnackbar }) => {
enqueueSnackbar(message, { variant });
return null;
});
const mountPoint = document.getElementById('snackbarhelper');
ReactDOM.render(
<SnackbarProvider maxSnack={3} anchorOrigin={{horizontal: 'left', vertical: 'bottom'}}>
<Display message={msg} variant={variant} />
</SnackbarProvider>,
mountPoint)
}
}
Then use like so:
import toast from '....'
toast.success("It works!")
As a sidenote/question: I first tried this with const mountPoint = document.createElement('div') and then mount it to an unattached DOMNode. This technique is from here. However, this didn't work - is this an issue with notistack or material-ui?
Thanks for sharing your solution @godmar.
I found @godmar's solution useful, since I wanted to do some logging from inside window.setInterval callbacks.
@iamhosseindhv maybe it would be worth mentioning this approach in the readme?
ps: instead of requiring snackbarhelper, we can of course just attach the node ourselves to the DOM.
Updated code:
import React from 'react';
import ReactDOM from 'react-dom';
import { SnackbarProvider, useSnackbar } from 'notistack';
// add a <div> child to body under which to mount the snackbars
const mountPoint = document.createElement('div');
document.body.appendChild(mountPoint);
export default {
success: function(msg) {
this.toast(msg, 'success');
},
warning: function(msg) {
this.toast(msg, 'warning');
},
info: function(msg) {
this.toast(msg, 'info');
},
error: function(msg) {
this.toast(msg, 'error');
},
toast: function(msg, variant = 'default') {
const ShowSnackbar = ({ message }) => {
const { enqueueSnackbar } = useSnackbar();
enqueueSnackbar(message, { variant });
return null;
};
ReactDOM.render(
// see https://github.com/iamhosseindhv/notistack#snackbarprovider
<SnackbarProvider
maxSnack={3}
anchorOrigin={{ horizontal: 'right', vertical: 'bottom' }}
>
<ShowSnackbar message={msg} variant={variant} />
</SnackbarProvider>,
mountPoint
);
}
};
Hey guys,
I did not like that ReactDOM.render() would be called every time.
Here is my take on it:
import { useSnackbar, VariantType, WithSnackbarProps } from 'notistack'
import React from 'react'
interface IProps {
setUseSnackbarRef: (showSnackbar: WithSnackbarProps) => void
}
const InnerSnackbarUtilsConfigurator: React.FC<IProps> = (props: IProps) => {
props.setUseSnackbarRef(useSnackbar())
return null
}
let useSnackbarRef: WithSnackbarProps
const setUseSnackbarRef = (useSnackbarRefProp: WithSnackbarProps) => {
useSnackbarRef = useSnackbarRefProp
}
export const SnackbarUtilsConfigurator = () => {
return <InnerSnackbarUtilsConfigurator setUseSnackbarRef={setUseSnackbarRef} />
}
export default {
success(msg: string) {
this.toast(msg, 'success')
},
warning(msg: string) {
this.toast(msg, 'warning')
},
info(msg: string) {
this.toast(msg, 'info')
},
error(msg: string) {
this.toast(msg, 'error')
},
toast(msg: string, variant: VariantType = 'default') {
useSnackbarRef.enqueueSnackbar(msg, { variant })
}
}
To initialize:
<SnackbarProvider maxSnack={3} anchorOrigin={{ horizontal: 'center', vertical: 'bottom' }}>
<SnackbarUtilsConfigurator />
...
</SnackbarProvider>
And then, anywhere in code:
import SnackbarUtils from 'src/utils/SnackbarUtils'
SnackbarUtils.success('Success π')
Also, with this implementation, you can expose anything the Hook or HOC was providing easily.
Cheers π₯
@joelbourbon I get
'InnerSnackbarUtilsConfigurator' refers to a value, but is being used as a type here.
when trying to add that code to a ts file
@mihai93 this line is a TSX line. You must have a *.tsx file.
Working Code Sandbox here.
Replied to a comment
Thanks a lot! π
I just updated a little bit your code:
export default {
success(msg: string, options: OptionsObject = {}) {
this.toast(msg, { ...options, variant: 'success' })
},
warning(msg: string, options: OptionsObject = {}) {
this.toast(msg, { ...options, variant: 'warning' })
},
info(msg: string, options: OptionsObject = {}) {
this.toast(msg, { ...options, variant: 'info' })
},
error(msg: string, options: OptionsObject = {}) {
this.toast(msg, { ...options, variant: 'error' })
},
toast(msg: string, options: OptionsObject = {}) {
useSnackbarRef.enqueueSnackbar(msg, options)
}
}
Reopening so people know support for it is going to be provided natively from notistack.
Replied to a comment
Unfortunately, maxSnack prop does not work when I fire the snackbars using this :/
EDIT: Ignore this comment, I was executing closeSnackbar() somewhere⦠my bad, I need to take a break.
@iamhosseindhv are you working on this yet?
a colleague and I came down with a simpler configurator:
let snackbarRef: WithSnackbarProps;
export const SnackbarUtilsConfigurator: React.FC = () => {
snackbarRef = useSnackbar();
return null;
};
π
Thanks for sharing your solution @godmar.
Hey guys,
I did not like that
ReactDOM.render()would be called every time.Here is my take on it:
import { useSnackbar, VariantType, WithSnackbarProps } from 'notistack' import React from 'react' interface IProps { setUseSnackbarRef: (showSnackbar: WithSnackbarProps) => void } const InnerSnackbarUtilsConfigurator: React.FC<IProps> = (props: IProps) => { props.setUseSnackbarRef(useSnackbar()) return null } let useSnackbarRef: WithSnackbarProps const setUseSnackbarRef = (useSnackbarRefProp: WithSnackbarProps) => { useSnackbarRef = useSnackbarRefProp } export const SnackbarUtilsConfigurator = () => { return <InnerSnackbarUtilsConfigurator setUseSnackbarRef={setUseSnackbarRef} /> } export default { success(msg: string) { this.toast(msg, 'success') }, warning(msg: string) { this.toast(msg, 'warning') }, info(msg: string) { this.toast(msg, 'info') }, error(msg: string) { this.toast(msg, 'error') }, toast(msg: string, variant: VariantType = 'default') { useSnackbarRef.enqueueSnackbar(msg, { variant }) } }To initialize:
<SnackbarProvider maxSnack={3} anchorOrigin={{ horizontal: 'center', vertical: 'bottom' }}> <SnackbarUtilsConfigurator /> ... </SnackbarProvider>And then, anywhere in code:
import SnackbarUtils from 'src/utils/SnackbarUtils' SnackbarUtils.success('Success π')Also, with this implementation, you can expose anything the Hook or HOC was providing easily.
Cheers π₯
Could you please provide the same solution without TS?
@ahmsalah
JS version is here.
The Easy way
Store the enqueueSnackbar & closeSnackbar in the some class variable at the time of startup of the application, And use anywhere in your application.
Follow the steps down below,
1.Store Both enqueueSnackbar & closeSnackbar to class variable inside the Routes.js file.
import React, { Component, useEffect, useState } from 'react';
import {Switch,Route, Redirect, useLocation} from 'react-router-dom';
import AppLayout from '../components/common/AppLayout';
import PrivateRoute from '../components/common/PrivateRoute';
import DashboardRoutes from './DashboardRoutes';
import AuthRoutes from './AuthRoutes';
import Auth from '../services/https/Auth';
import store from '../store';
import { setCurrentUser } from '../store/user/action';
import MySpinner from '../components/common/MySpinner';
import { SnackbarProvider, useSnackbar } from "notistack";
import SnackbarUtils from '../utils/SnackbarUtils';
const Routes = () => {
const location = useLocation()
const [authLoading,setAuthLoading] = useState(true)
//1. UseHooks to get enqueueSnackbar, closeSnackbar
const { enqueueSnackbar, closeSnackbar } = useSnackbar();
useEffect(()=>{
//2. Store both enqueueSnackbar & closeSnackbar to class variables
SnackbarUtils.setSnackBar(enqueueSnackbar,closeSnackbar)
const currentUser = Auth.getCurrentUser()
store.dispatch(setCurrentUser(currentUser))
setAuthLoading(false)
},[])
if(authLoading){
return(
<MySpinner title="Authenticating..."/>
)
}
return (
<AppLayout
noLayout={location.pathname=="/auth/login"||location.pathname=="/auth/register"}
>
<div>
<Switch>
<Redirect from="/" to="/auth" exact/>
<PrivateRoute redirectWithAuthCheck={true} path = "/auth" component={AuthRoutes}/>
<PrivateRoute path = "/dashboard" component={DashboardRoutes}/>
<Redirect to="/auth"/>
</Switch>
</div>
</AppLayout>
);
}
export default Routes;
2. This is how SnackbarUtils.js file looks like
class SnackbarUtils {
#snackBar = {
enqueueSnackbar: ()=>{},
closeSnackbar: () => {},
};
setSnackBar(enqueueSnackbar, closeSnackbar) {
this.#snackBar.enqueueSnackbar = enqueueSnackbar;
this.#snackBar.closeSnackbar = closeSnackbar;
}
success(msg, options = {}) {
return this.toast(msg, { ...options, variant: "success" });
}
warning(msg, options = {}) {
return this.toast(msg, { ...options, variant: "warning" });
}
info(msg, options = {}) {
return this.toast(msg, { ...options, variant: "info" });
}
error(msg, options = {}) {
return this.toast(msg, { ...options, variant: "error" });
}
toast(msg, options = {}) {
const finalOptions = {
variant: "default",
...options,
};
return this.#snackBar.enqueueSnackbar(msg, { ...finalOptions });
}
closeSnackbar(key) {
this.#snackBar.closeSnackbar(key);
}
}
export default new SnackbarUtils();
3.Now just import the SnackbarUtils and use snackbar anywhere in your application as follows.
<button onClick={()=>{
SnackbarUtils.success("Hello")
}}>Show</button>
You can use snackbar in non react component file also
happy coding π
I allowed myself to present a JS version of the TSX solution by @joelbourbon whom I thank for the good job by the way π
First, declare a file called SnackBarUtils.js inside a utils folder or do it your way and set the following code inside
import { useSnackbar } from 'notistack'
import React from 'react'
const InnerSnackbarUtilsConfigurator = (props) => {
props.setUseSnackbarRef(useSnackbar())
return null;
}
let useSnackbarRef;
const setUseSnackbarRef = (useSnackbarRefProp) => {
useSnackbarRef = useSnackbarRefProp
}
export const SnackbarUtilsConfigurator = () => {
return <InnerSnackbarUtilsConfigurator setUseSnackbarRef={setUseSnackbarRef} />
}
export const snackActions = {
success(msg) {
this.toast(msg, 'success')
},
warning(msg) {
this.toast(msg, 'warning')
},
info(msg) {
this.toast(msg, 'info')
},
error(msg) {
this.toast(msg, 'error')
},
toast(msg, variant = 'default') {
useSnackbarRef.enqueueSnackbar(msg, { variant })
}
}
Import SnackbarUtilsConfigurator inside your entry component ( by default in React apps -> src/index.js ) like in the code below
import { SnackbarUtilsConfigurator } from "./utils/SnackbarUtils";
ReactDOM.render(
<StoreProvider store={store}>
<SnackbarProvider maxSnack={2}>
<SnackbarUtilsConfigurator />
<App />
</SnackbarProvider>
</StoreProvider>,
document.getElementById("root")
);
Finally, import snackActions from wherever you have your SnackBarUtils.js file.
In the following example, I have a model for my easy-peasy store ( it's a better and cleaner implementation of a redux store imo ), I fetch a list of projects and once it's done loading I show a success message. Of course this is just a basic case, you can customize your messages by modifying the SnackBarUtils file and calling snackActions with different parameters
import { snackActions } from '../utils/SnackbarUtils';
getProjects: thunk(async (actions, payload) => {
actions.setSidebarIsLoadingProjects(true);
const projects = await getProjects();
if (!projects) {
actions.setProjects([]);
actions.setSidebarIsLoadingProjects(false);
snackActions.error('Failed to fetch projects');
} else {
actions.setProjects(projects);
actions.setSidebarIsLoadingProjects(false);
snackActions.success('Succsefully fetched projects');
}
})
Just implemented TSX my variant of solutions presented above. Decided to share it here, because:
globallyAvailableNotifications.enqueueErrorNotification(β¦) anywhere in the code without worrying of undefined globallyStoredSnackbarSnackbarUtilsConfigurator + InnerSnackbarUtilsConfigurator)useNotifications) and hacky-but-diable usage (lengthy and more explicit in name globallyAvailableNotifications)notistack "namespace" (due to * as notistack import)import React from "react";
import * as notistack from "notistack";
import { makeStyles as makeMuiStyles } from "@material-ui/core/styles";
export interface Notifications {
enqueueErrorNotification: (errorMessage: string) => void;
}
export const useNotifications = (): Notifications => {
const snackbar = notistack.useSnackbar();
return {
enqueueErrorNotification: (errorMessage) => {
snackbar.enqueueSnackbar(errorMessage, {
variant: "error",
});
},
};
};
// Notifications available outside components, based on https://github.com/iamhosseindhv/notistack/issues/30#issuecomment-542863653
let globallyStoredSnackbar: notistack.ProviderContext | undefined;
export const globallyAvailableNotifications: Notifications = {
enqueueErrorNotification: (errorMessage) => {
globallyStoredSnackbar?.enqueueSnackbar(errorMessage, {
variant: "error",
});
},
};
export const NotificationsProvider: React.FC = ({ children }) => {
const classes = useStyles();
const GloballyStoredSnackbarProvider: React.FC = React.useMemo(
() => () => {
globallyStoredSnackbar = notistack.useSnackbar();
return null;
},
[],
);
return (
<notistack.SnackbarProvider
maxSnack={3}
autoHideDuration={5000}
hideIconVariant
classes={{
variantError: classes.error,
}}
>
<GloballyStoredSnackbarProvider />
{children}
</notistack.SnackbarProvider>
);
};
const useStyles = makeMuiStyles(() => ({
error: {
backgroundColor: `rgb(255, 0, 0) !important`,
},
}));
I allowed myself to present a JS version of the TSX solution by @joelbourbon whom I thank for the good job by the way +1
First, declare a file called SnackBarUtils.js inside a utils folder or do it your way and set the following code insideimport { useSnackbar } from 'notistack' import React from 'react' const InnerSnackbarUtilsConfigurator = (props) => { props.setUseSnackbarRef(useSnackbar()) return null; } let useSnackbarRef; const setUseSnackbarRef = (useSnackbarRefProp) => { useSnackbarRef = useSnackbarRefProp } export const SnackbarUtilsConfigurator = () => { return <InnerSnackbarUtilsConfigurator setUseSnackbarRef={setUseSnackbarRef} /> } export const snackActions = { success(msg) { this.toast(msg, 'success') }, warning(msg) { this.toast(msg, 'warning') }, info(msg) { this.toast(msg, 'info') }, error(msg) { this.toast(msg, 'error') }, toast(msg, variant = 'default') { useSnackbarRef.enqueueSnackbar(msg, { variant }) } }Import SnackbarUtilsConfigurator inside your entry component ( by default in React apps -> src/index.js ) like in the code below
import { SnackbarUtilsConfigurator } from "./utils/SnackbarUtils"; ReactDOM.render( <StoreProvider store={store}> <SnackbarProvider maxSnack={2}> <SnackbarUtilsConfigurator /> <App /> </SnackbarProvider> </StoreProvider>, document.getElementById("root") );Finally, import snackActions from wherever you have your SnackBarUtils.js file.
In the following example, I have a model for my easy-peasy store ( it's a better and cleaner implementation of a redux store imo ), I fetch a list of projects and once it's done loading I show a success message. Of course this is just a basic case, you can customize your messages by modifying the SnackBarUtils file and calling snackActions with different parametersimport { snackActions } from '../utils/SnackbarUtils'; getProjects: thunk(async (actions, payload) => { actions.setSidebarIsLoadingProjects(true); const projects = await getProjects(); if (!projects) { actions.setProjects([]); actions.setSidebarIsLoadingProjects(false); snackActions.error('Failed to fetch projects'); } else { actions.setProjects(projects); actions.setSidebarIsLoadingProjects(false); snackActions.success('Succsefully fetched projects'); } })
@ethubert, thanks for sharing the code. I might have missing something but I think that we don't need to create component inside component like in SnackBarUtils.js.
It's enough to
let useSnackbarRef = null;
export const SnackbarUtilsConfigurator = () => {
useSnackbarRef = useSnackbar();
return null;
};
Most helpful comment
Hey guys,
I did not like that
ReactDOM.render()would be called every time.Here is my take on it:
To initialize:
And then, anywhere in code:
Also, with this implementation, you can expose anything the Hook or HOC was providing easily.
Cheers π₯