diff --git a/src/index.tsx b/src/index.tsx index 15255f0..4e8c6e5 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -549,19 +549,33 @@ function useSonner() { React.useEffect(() => { return ToastState.subscribe((toast) => { - setActiveToasts((currentToasts) => { - if ('dismiss' in toast && toast.dismiss) { - return currentToasts.filter((t) => t.id !== toast.id); - } + if ((toast as ToastToDismiss).dismiss) { + setTimeout(() => { + ReactDOM.flushSync(() => { + setActiveToasts((toasts) => toasts.filter((t) => t.id !== toast.id)); + }); + }); + return; + } - const existingToastIndex = currentToasts.findIndex((t) => t.id === toast.id); - if (existingToastIndex !== -1) { - const updatedToasts = [...currentToasts]; - updatedToasts[existingToastIndex] = { ...updatedToasts[existingToastIndex], ...toast }; - return updatedToasts; - } else { - return [toast, ...currentToasts]; - } + // Prevent batching, temp solution. + setTimeout(() => { + ReactDOM.flushSync(() => { + setActiveToasts((toasts) => { + const indexOfExistingToast = toasts.findIndex((t) => t.id === toast.id); + + // Update the toast if it already exists + if (indexOfExistingToast !== -1) { + return [ + ...toasts.slice(0, indexOfExistingToast), + { ...toasts[indexOfExistingToast], ...toast }, + ...toasts.slice(indexOfExistingToast + 1), + ]; + } + + return [toast, ...toasts]; + }); + }); }); }); }, []); diff --git a/src/state.ts b/src/state.ts index 7db5492..cc5f3bd 100644 --- a/src/state.ts +++ b/src/state.ts @@ -9,10 +9,12 @@ type titleT = (() => React.ReactNode) | React.ReactNode; class Observer { subscribers: Array<(toast: ExternalToast | ToastToDismiss) => void>; toasts: Array; + dismissedToasts: Set; constructor() { this.subscribers = []; this.toasts = []; + this.dismissedToasts = new Set(); } // We use arrow functions to maintain the correct `this` reference @@ -49,6 +51,10 @@ class Observer { }); const dismissible = data.dismissible === undefined ? true : data.dismissible; + if (this.dismissedToasts.has(id)) { + this.dismissedToasts.delete(id); + } + if (alreadyExists) { this.toasts = this.toasts.map((toast) => { if (toast.id === id) { @@ -72,12 +78,13 @@ class Observer { }; dismiss = (id?: number | string) => { + this.dismissedToasts.add(id); + if (!id) { this.toasts.forEach((toast) => { this.subscribers.forEach((subscriber) => subscriber({ id: toast.id, dismiss: true })); }); } - this.subscribers.forEach((subscriber) => subscriber({ id, dismiss: true })); return id; }; @@ -189,6 +196,10 @@ class Observer { this.create({ jsx: jsx(id), id, ...data }); return id; }; + + getActiveToasts = () => { + return this.toasts.filter((toast) => !this.dismissedToasts.has(toast.id)); + }; } export const ToastState = new Observer(); @@ -219,6 +230,7 @@ const isHttpResponse = (data: any): data is Response => { const basicToast = toastFunction; const getHistory = () => ToastState.toasts; +const getToasts = () => ToastState.getActiveToasts(); // We use `Object.assign` to maintain the correct types as we would lose them otherwise export const toast = Object.assign( @@ -234,5 +246,5 @@ export const toast = Object.assign( dismiss: ToastState.dismiss, loading: ToastState.loading, }, - { getHistory }, + { getHistory, getToasts }, );