From 4e2b71cf9f8bd1d69477f13921b952fdf681735a Mon Sep 17 00:00:00 2001 From: Thomas Joise Date: Fri, 17 Jul 2026 12:16:59 +0530 Subject: [PATCH] fix(ui): toast auto-dismiss id derived inside the state updater (React 18 batching) Co-Authored-By: Claude Opus 4.8 (1M context) --- packages/ui/src/toast.tsx | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/packages/ui/src/toast.tsx b/packages/ui/src/toast.tsx index 98d0c5c..5fe5cb4 100644 --- a/packages/ui/src/toast.tsx +++ b/packages/ui/src/toast.tsx @@ -1,4 +1,4 @@ -import { createContext, useCallback, useContext, useMemo, useRef, useState, type ReactNode } from 'react' +import { createContext, useCallback, useContext, useMemo, useState, type ReactNode } from 'react' import { dismissToast, initialToasts, pushToast, type Toast, type ToastState } from './toast-store' const AUTO_DISMISS_MS = 4000 @@ -9,13 +9,14 @@ const Ctx = createContext(null) /** Mount once at app root; renders the bottom-right stack. */ export function ToastProvider(props: { children: ReactNode }) { const [state, setState] = useState(initialToasts) - const stateRef = useRef(state) - stateRef.current = state const push = useCallback((tone: Toast['tone'], message: string) => { - const id = stateRef.current.nextId - setState((s) => pushToast(s, tone, message)) - window.setTimeout(() => setState((s) => dismissToast(s, id)), AUTO_DISMISS_MS) + setState((s) => { + const next = pushToast(s, tone, message) + const id = next.toasts[next.toasts.length - 1]!.id + window.setTimeout(() => setState((s2) => dismissToast(s2, id)), AUTO_DISMISS_MS) + return next + }) }, []) const api = useMemo(() => ({