Skip to content

Commit

Permalink
fix: useSonner hook (#549)
Browse files Browse the repository at this point in the history
* fix use sonner hook

* rename util
  • Loading branch information
emilkowalski authored Jan 29, 2025
1 parent 7d017fc commit 508a1c5
Show file tree
Hide file tree
Showing 2 changed files with 40 additions and 14 deletions.
38 changes: 26 additions & 12 deletions src/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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];
});
});
});
});
}, []);
Expand Down
16 changes: 14 additions & 2 deletions src/state.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,12 @@ type titleT = (() => React.ReactNode) | React.ReactNode;
class Observer {
subscribers: Array<(toast: ExternalToast | ToastToDismiss) => void>;
toasts: Array<ToastT | ToastToDismiss>;
dismissedToasts: Set<string | number>;

constructor() {
this.subscribers = [];
this.toasts = [];
this.dismissedToasts = new Set();
}

// We use arrow functions to maintain the correct `this` reference
Expand Down Expand Up @@ -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) {
Expand All @@ -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;
};
Expand Down Expand Up @@ -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();
Expand Down Expand Up @@ -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(
Expand All @@ -234,5 +246,5 @@ export const toast = Object.assign(
dismiss: ToastState.dismiss,
loading: ToastState.loading,
},
{ getHistory },
{ getHistory, getToasts },
);

0 comments on commit 508a1c5

Please sign in to comment.