Notistack: Be able to add a close snack bar action button to SnackBarProvider

Created on 21 Aug 2019  ·  6Comments  ·  Source: iamhosseindhv/notistack

Expected Behavior

This is more of a feature request, but it would be very helpful and save a lot of boilerplate code to be able to add a dismiss button as the action prop in the SnackBarProvider.

Current Behavior

There is no way to define a dismiss button as the action in the SnackBarProvider since the closeSnackbar function is not accessible at the same level as the SnackBarProvider (Unless I missed something??).

Context

I really like this library and would like to replace the current notification dependency we are using with it. We have to have a dismiss button/icon on all of our notifications though and there is a lot of added boilerplate to have to add the additional action button to every single enqueSnackbar call.

I was trying to think of a few different solutions but wasn't sure which was the most practical. Passing a reference to the close function in addition to the key in the finalAction might work. Or adding a separate prop to the SnackBarProvider to show a dismiss button could be another option. Adding a global SnackItem "template" similar to the children prop in enqueSnackbar could be another solution. But in that case you would still want access to all other other props like variant.

Most helpful comment

For the record, what you are looking for used to be a default. You should be able to implement it back this way:

import { IconButton } from '@material-ui/core';
import { Close as IconClose } from '@material-ui/icons';
import { useSnackbar } from 'notistack';
import * as React from 'react';

function SnackbarCloseButton({ key }) {
  const { closeSnackbar } = useSnackbar();

  return (
    <IconButton onClick={() => closeSnackbar(key)}>
      <IconClose />
    </IconButton>
  );
}

export default SnackbarCloseButton;
<SnackbarProvider action={key => <SnackbarCloseButton key={key} />}>
  ...
</SnackbarProvider>

You are right about the context. The thing is that, although the action is passed at the same level as the creation of the context, the action itself is only created further down in the tree at a point where the context is available.

All 6 comments

For the record, what you are looking for used to be a default. You should be able to implement it back this way:

import { IconButton } from '@material-ui/core';
import { Close as IconClose } from '@material-ui/icons';
import { useSnackbar } from 'notistack';
import * as React from 'react';

function SnackbarCloseButton({ key }) {
  const { closeSnackbar } = useSnackbar();

  return (
    <IconButton onClick={() => closeSnackbar(key)}>
      <IconClose />
    </IconButton>
  );
}

export default SnackbarCloseButton;
<SnackbarProvider action={key => <SnackbarCloseButton key={key} />}>
  ...
</SnackbarProvider>

You are right about the context. The thing is that, although the action is passed at the same level as the creation of the context, the action itself is only created further down in the tree at a point where the context is available.

Ah yes, this is great! Thank your for the assist! I just had to change the key prop to id since key is a restricted word. Thanks again!

The following works too, without creating additional components.. Are there any downsides ?

const notistackRef = useRef();

<SnackbarProvider
            ref={notistackRef}
            action={(key) => (
                <Button
                    onClick={() => notistackRef.current.closeSnackbar(key)}
                    style={{ color: '#fff', fontSize: '20px' }}
                >
                    ✖
                </Button>
            )}
>
...
</SnackbarProvider>

@Zhouzi Do you know when and why the default was changed ? Thanks

The following works too, without creating additional components.. Are there any downsides ?

const notistackRef = useRef();

<SnackbarProvider
            ref={notistackRef}
            action={(key) => (
                <Button
                    onClick={() => notistackRef.current.closeSnackbar(key)}
                    style={{ color: '#fff', fontSize: '20px' }}
                >
                    ✖
                </Button>
            )}
>
...
</SnackbarProvider>

Unless documented, I would consider a component's methods a private API. For example, it would break if the methods become private or if SnackbarProvider becomes a function component.

@Zhouzi Do you know when and why the default was changed ? Thanks

I wish I had given that information in my first comment. Sadly, I can't remember when it was removed (and I probably never knew why). From what I recall, in the early versions of the library you could provide a default component for the close button. Something like: <SnackbarProvider action={<button>close</button>} />. And it would "just work™️".

To be fair, I think it was a good choice to remove it. The new API has no magic and is easier to control.

Ok thanks.

Also if the SnackbarProvider ref changes one day, we can always do something like:

const GimmeTheRefAlready = (props) => {
  props.setref(useSnackbar());
  return null;
}
...
const ref = useRef();
<SnackbarProvider>
<GimmeTheRefAlready setref={(notistack) => {ref.current=notistack}}/>
...
</SnackbarProvider>

I wonder if we can simplify this further

Was this page helpful?
0 / 5 - 0 ratings

Related issues

VincentLanglet picture VincentLanglet  ·  5Comments

Mel0nHead picture Mel0nHead  ·  5Comments

nebojsanb picture nebojsanb  ·  3Comments

seannguyn picture seannguyn  ·  4Comments

james-cordeiro picture james-cordeiro  ·  6Comments