React-toastify showing multiple toast
Asked Answered
H

9

8

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.sstatic.net/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.

Highcolored answered 25/6, 2020 at 14:47 Comment(2)
Move <ToastContainer /> outside of <Layout />Ghana
I don't believe that. So simple and I couldn't see. Thank you very much, it worked great. If you want to edit your comment as an answer I'll be glad to accept it.Highcolored
G
10

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

Ghana answered 25/6, 2020 at 14:56 Comment(0)
B
35

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.

Backstairs answered 23/9, 2021 at 15:42 Comment(4)
Fixed my issue, thanks!Sunstroke
This fixed my issue but how does it works ? What exactly happened by adding toastId ?Acetic
@BilalMohammad I don't know the underlying code, but this is one recommended solution from the documentation: fkhadra.github.io/react-toastify/prevent-duplicateBackstairs
Just a note to anyone seeing this: I haven't investigated the underlying issue, but I'm pretty sure looking back was that we toasted in a useEffect and had React in strict mode, which renders all hooks twice.Backstairs
G
10

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

Ghana answered 25/6, 2020 at 14:56 Comment(0)
N
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

Narah answered 25/6, 2020 at 15:2 Comment(1)
Thank you. It really makes sense.Highcolored
D
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>
)
Difficult answered 12/5, 2021 at 12:14 Comment(0)
U
2

add toastId:

toast.success('Produto removido com sucesso', {
    toastId: 'success1',
})
Uzziel answered 27/6, 2023 at 10:1 Comment(0)
T
1

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

Transmigrate answered 5/8, 2022 at 7:50 Comment(1)
This suggestion solved my problem. I thought you needed a ToastContainter in every component where you called 'toast' function. Instead, I added a single <ToastContainer /> at the root of my app, and all my toasts in children components worked great. Thank you.Ramonitaramos
Y
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!

Yawn answered 27/9, 2022 at 19:38 Comment(0)
I
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/

Individuality answered 21/2, 2023 at 11:42 Comment(0)
I
1

You Just need to control the of toaster and it will resolve the issue

<ToastContainer
         position="top-right"
         autoClose={5000}
         hideProgressBar={false}
         newestOnTop={false}
         closeOnClick
         rtl={false}
         limit={1}
         pauseOnFocusLoss
         draggable
         pauseOnHover
/>

In React Application when you called the Toaster Component. Make the Prop "autoClose" as 5000 like shown in code above. It will resolve the issue.

Incompliant answered 24/10, 2023 at 14:38 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.