diff --git a/src/index.tsx b/src/index.tsx index ce031e8..e6d49d2 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -683,7 +683,7 @@ const Toaster = React.forwardRef(function Toaster(pro }); }); }); - }, [toasts]); + }, []); React.useEffect(() => { if (theme !== 'system') { diff --git a/src/state.ts b/src/state.ts index b925eca..e8a7622 100644 --- a/src/state.ts +++ b/src/state.ts @@ -29,9 +29,18 @@ class Observer { subscribe = (subscriber: (toast: ToastT | ToastToDismiss) => void) => { this.subscribers.push(subscriber); + // Replay toasts queued before this subscriber attached (issue #723). + for (const toast of this.toasts) { + if ('dismiss' in toast && (toast as ToastToDismiss).dismiss) continue; + if (this.dismissedToasts.has(toast.id)) continue; + subscriber(toast); + } + return () => { const index = this.subscribers.indexOf(subscriber); - this.subscribers.splice(index, 1); + if (index !== -1) { + this.subscribers.splice(index, 1); + } }; }; diff --git a/test/src/app/issue-723/page.tsx b/test/src/app/issue-723/page.tsx new file mode 100644 index 0000000..301b412 --- /dev/null +++ b/test/src/app/issue-723/page.tsx @@ -0,0 +1,25 @@ +'use client'; + +import React from 'react'; +import { Toaster, toast } from 'sonner'; + +// Sibling rendered ABOVE — its useEffect runs before the Toaster's, +// so toast() fires before ToastState has a subscriber. See issue #723. +function ToastOnMount() { + const fired = React.useRef(false); + React.useEffect(() => { + if (fired.current) return; + fired.current = true; + toast('Toast fired before Toaster subscribed'); + }, []); + return null; +} + +export default function Issue723Page() { + return ( + <> + + + + ); +} diff --git a/test/tests/basic.spec.ts b/test/tests/basic.spec.ts index 34f6e01..f922515 100644 --- a/test/tests/basic.spec.ts +++ b/test/tests/basic.spec.ts @@ -337,4 +337,10 @@ test.describe('Basic functionality', () => { await expect(page.getByTestId('promise-test-toast')).toHaveText('Loading...'); await expect(page.getByTestId('promise-test-toast')).toHaveText('Loaded'); }); + + // Regression for https://github.com/emilkowalski/sonner/issues/723 + test('toast fired before Toaster subscribes is still displayed', async ({ page }) => { + await page.goto('/issue-723'); + await expect(page.getByText('Toast fired before Toaster subscribed')).toHaveCount(1); + }); });