fix(ui): toast auto-dismiss id derived inside the state updater (React 18 batching)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
feat/client-detail-redesign
Thomas Joise 5 days ago
parent 03402ba0e1
commit 4e2b71cf9f

@ -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' import { dismissToast, initialToasts, pushToast, type Toast, type ToastState } from './toast-store'
const AUTO_DISMISS_MS = 4000 const AUTO_DISMISS_MS = 4000
@ -9,13 +9,14 @@ const Ctx = createContext<ToastApi | null>(null)
/** Mount once at app root; renders the bottom-right stack. */ /** Mount once at app root; renders the bottom-right stack. */
export function ToastProvider(props: { children: ReactNode }) { export function ToastProvider(props: { children: ReactNode }) {
const [state, setState] = useState<ToastState>(initialToasts) const [state, setState] = useState<ToastState>(initialToasts)
const stateRef = useRef(state)
stateRef.current = state
const push = useCallback((tone: Toast['tone'], message: string) => { const push = useCallback((tone: Toast['tone'], message: string) => {
const id = stateRef.current.nextId setState((s) => {
setState((s) => pushToast(s, tone, message)) const next = pushToast(s, tone, message)
window.setTimeout(() => setState((s) => dismissToast(s, id)), AUTO_DISMISS_MS) const id = next.toasts[next.toasts.length - 1]!.id
window.setTimeout(() => setState((s2) => dismissToast(s2, id)), AUTO_DISMISS_MS)
return next
})
}, []) }, [])
const api = useMemo<ToastApi>(() => ({ const api = useMemo<ToastApi>(() => ({

Loading…
Cancel
Save