3

I am building a React app with several components and in at least half of them I am using React-notify and it's working properly in almost all of them except one. In this one when I trigger the toast I'm getting four toasts, one behind the other, but I believe they are not different toasts, since they have the same ID.

I found this thread https://github.com/fkhadra/react-toastify/issues/182, here the user was having the same issue as mine, the only exception is that I am not setting autoclose, he even provided a gif showing the problem:

https://i.stack.imgur.com/SiqRo.jpg

The solution according to this thread would be remove all <ToastContainer /> of the components and render it in the app root, which in my case is App.js. I did that, however the toasts are not being shown anymore, I don't know if I've done it right, though.

Besides that I also tried to set a custom ID and it didn't change anything.

I am using React-router-dom, maybe this is influencing in something, I couldn't find a proper answer nor in the documentation neither on any other source.

Here is simplified version of my App.js:

import Layout from './containers/Layout/Layout';

import { ToastContainer } from 'react-toastify';
import 'react-toastify/dist/ReactToastify.css';

import { BrowserRouter, Route, Switch } from 'react-router-dom';

class App extends Component {
  render() {
    return (
      <BrowserRouter>
        <Layout>
          <Switch>
            <Route path="/clientes" exact component={ClientesControls} />
            <Route path="/adm" exact component={AdmControls} />
            <Route path="/" component={OrcConfig} />
            <ToastContainer />
          </Switch>
        </Layout>
      </BrowserRouter>
    );
  }
}

Here is a sample of the componente whose bug is being generated:

import React from 'react';

import axios from '../../../axios';

import { toast } from 'react-toastify';

const listarProdutosItens = props => {
    
    const excluirItemHandler = i => {
        
        let key = props.listaItens[i].key
        let categoria = props.listaItens[i].categoria

        axios.delete(`/adm/${categoria}/${key}.json`)
            .then(res => {
                props.fetchLista()
                notify('excluído')
            })
            .catch(error => notify('não excluído'))
    }

    const notify = (arg) => {
        if (arg === 'excluído') {
            toast.success('Produto removido com sucesso')
            console.log('TESTE')
        } else if (arg === 'não excluído') {
            toast.error('Erro ao tentar remover produto')
        }
    }

    return (
        <div className="row border-bottom mt-2">
            <button onClick={() => excluirItemHandler(i)} ></button>
            {/* <ToastContainer /> */}
        </div>
    )

}

The components that are working properly have the same sintaxe.

Any help would be very appreciated.

Berg_Durden
  • 1,531
  • 4
  • 24
  • 46

8 Answers8

26

I was having this same problem (and my was already outside the router stuff). This probably doesn't fix the underlying issue, but what worked for me was to add a custom toast id, that is change

toast.success('Produto removido com sucesso')

to

toast.success('Produto removido com sucesso', {
    toastId: 'success1',
})

and the duplicate toasts no longer showed up.

Klaus Haukenstein
  • 570
  • 1
  • 5
  • 10
9

Just move <ToastContainer /> outside of <Layout />

demkovych
  • 7,827
  • 3
  • 19
  • 25
6

Move the <ToastContainer/> anywhere outside of <Switch>, because:

<Switch> is unique in that it renders a route exclusively.

Also:

All children of a <Switch> should be <Route> or <Redirect> elements. Only the first child to match the current location will be rendered.

See: https://reacttraining.com/react-router/web/api/Switch

Guillermo Gutiérrez
  • 17,273
  • 17
  • 89
  • 116
2

import the toast in your component where you have added toaster logic ex. given below:

import { toast } from 'react-toastify';
// avoid the ToastContainer to add here and the css as well

Then at the root of your application:

import { ToastContainer } from 'react-toastify';
import 'react-toastify/dist/ReactToastify.css';

const CommonComponent = () => (
  <div>
    <ToastContainer />
    <OtherComponent />
  </div>
)
Jitendra Rathor
  • 607
  • 8
  • 11
1

You must also certainly check that your code does not have multiple <ToastContainer/> in different places of your application

RVK
  • 43
  • 1
  • 7
  • This suggestion solved my problem. I thought you needed a ToastContainter in every component where you called 'toast' function. Instead, I added a single at the root of my app, and all my toasts in children components worked great. Thank you. – NicoWheat Aug 27 '22 at 22:41
1
const notify = (arg) => {
    if (arg === 'excluído') {
        toast.success('Produto removido com sucesso')
        console.log('TESTE')
    } else if (arg === 'não excluído') {
        toast.error('Erro ao tentar remover produto')
    }
}

To

const notify = (arg) => {
    if (arg === 'excluído') {
        toast.success('Produto removido com sucesso', {
        toastId: "success"        
    })
   
    } else if (arg === 'não excluído') {
        toast.error('Erro ao tentar remover produto', {
        toastId: "error"        
    })
    }
}

Just use custom toastId and it will fix your issue!

0

add toastId:

toast.success('Produto removido com sucesso', {
    toastId: 'success1',
})
-1

Just add limit={1} to ToastContainer. Like this:

<ToastContainer limit={1}>

Reference: https://fkhadra.github.io/react-toastify/limit-the-number-of-toast-displayed/

double-beep
  • 5,031
  • 17
  • 33
  • 41