$ import { useState, useEffect, useCallback } from 'react'; import SomeSingleton from '@/singletons/some'; export function ReactElement() { const [singletonData, setSingletonData] = useState(SomeSingleton.data || null); /** * Sync singleton and state */ const handleRefresh = useCallback(() => { setSingletonData(SomeSingleton.data || null) }, []); // Trigger refresh every 5 seconds useEffect(() => { const interval = window.setInterval(handleRefresh, 5000); return () => window.clearInterval(interval); }, [handleRefresh]); return ( <div> <span>{singletonData || 'N/A'}</span> <button onClick={handleRefresh}>Manual Refresh</button> </div> ) } COMMAND_BLOCK: import { useState, useEffect, useCallback } from 'react'; import SomeSingleton from '@/singletons/some'; export function ReactElement() { const [singletonData, setSingletonData] = useState(SomeSingleton.data || null); /** * Sync singleton and state */ const handleRefresh = useCallback(() => { setSingletonData(SomeSingleton.data || null) }, []); // Trigger refresh every 5 seconds useEffect(() => { const interval = window.setInterval(handleRefresh, 5000); return () => window.clearInterval(interval); }, [handleRefresh]); return ( <div> <span>{singletonData || 'N/A'}</span> <button onClick={handleRefresh}>Manual Refresh</button> </div> ) } COMMAND_BLOCK: import { useState, useEffect, useCallback } from 'react'; import SomeSingleton from '@/singletons/some'; export function ReactElement() { const [singletonData, setSingletonData] = useState(SomeSingleton.data || null); /** * Sync singleton and state */ const handleRefresh = useCallback(() => { setSingletonData(SomeSingleton.data || null) }, []); // Trigger refresh every 5 seconds useEffect(() => { const interval = window.setInterval(handleRefresh, 5000); return () => window.clearInterval(interval); }, [handleRefresh]); return ( <div> <span>{singletonData || 'N/A'}</span> <button onClick={handleRefresh}>Manual Refresh</button> </div> ) } COMMAND_BLOCK: import { useState, useEffect, useCallback } from 'react'; import SomeSingleton from '@/singletons/some'; export function ReactElement() { const [singletonData, setSingletonData] = useState(SomeSingleton.data || null); // Update the state as soon as things change in the singleton useEffect(() => { const ac = new AbortController(); SomeSingleton.addEventListener('change', ({detail})=>{ setSingletonData(detail); }, {signal: ac.signal}) return () => ac.abort(); }, []); return ( <div> <span>{singletonData || 'N/A'}</span> </div> ) } COMMAND_BLOCK: import { useState, useEffect, useCallback } from 'react'; import SomeSingleton from '@/singletons/some'; export function ReactElement() { const [singletonData, setSingletonData] = useState(SomeSingleton.data || null); // Update the state as soon as things change in the singleton useEffect(() => { const ac = new AbortController(); SomeSingleton.addEventListener('change', ({detail})=>{ setSingletonData(detail); }, {signal: ac.signal}) return () => ac.abort(); }, []); return ( <div> <span>{singletonData || 'N/A'}</span> </div> ) } COMMAND_BLOCK: import { useState, useEffect, useCallback } from 'react'; import SomeSingleton from '@/singletons/some'; export function ReactElement() { const [singletonData, setSingletonData] = useState(SomeSingleton.data || null); // Update the state as soon as things change in the singleton useEffect(() => { const ac = new AbortController(); SomeSingleton.addEventListener('change', ({detail})=>{ setSingletonData(detail); }, {signal: ac.signal}) return () => ac.abort(); }, []); return ( <div> <span>{singletonData || 'N/A'}</span> </div> ) } COMMAND_BLOCK: export interface Toast { id: string; message: string; type: "info" | "success" | "loading" | "error"; action?: { label: string; callback: () => void; }; } type ToastEvents = { changed: void; }; COMMAND_BLOCK: export interface Toast { id: string; message: string; type: "info" | "success" | "loading" | "error"; action?: { label: string; callback: () => void; }; } type ToastEvents = { changed: void; }; COMMAND_BLOCK: export interface Toast { id: string; message: string; type: "info" | "success" | "loading" | "error"; action?: { label: string; callback: () => void; }; } type ToastEvents = { changed: void; }; COMMAND_BLOCK: class ToastManager extends TypedEventTarget<ToastEvents> { private _toasts: Toast[] = []; private _timers = new Map<string, number>(); } COMMAND_BLOCK: class ToastManager extends TypedEventTarget<ToastEvents> { private _toasts: Toast[] = []; private _timers = new Map<string, number>(); } COMMAND_BLOCK: class ToastManager extends TypedEventTarget<ToastEvents> { private _toasts: Toast[] = []; private _timers = new Map<string, number>(); } CODE_BLOCK: get toasts() { return this._toasts; } private set toasts(value: Toast[]) { this._toasts = [...value]; this.dispatchEvent("changed"); } CODE_BLOCK: get toasts() { return this._toasts; } private set toasts(value: Toast[]) { this._toasts = [...value]; this.dispatchEvent("changed"); } CODE_BLOCK: get toasts() { return this._toasts; } private set toasts(value: Toast[]) { this._toasts = [...value]; this.dispatchEvent("changed"); } COMMAND_BLOCK: // add or -weight: 500;">update a toast item add = (toast: Omit<Toast, "id"> & { id?: string }, duration = 3000) => { const id = toast.id ?? Math.random().toString(36).substring(2, 9); this.clearTimer(id); const newToast = { ...toast, id }; const exists = this.toasts.some((t) => t.id === id); if (exists) { this.toasts = this.toasts.map((t) => (t.id === id ? newToast : t)); } else { this.toasts = [...this.toasts, newToast]; } if (duration > 0) { const timer = window.setTimeout(() => this.-weight: 500;">remove(id), duration); this._timers.set(id, timer); } return id; }; // -weight: 500;">remove a toast and its timer -weight: 500;">remove = (id: string) => { this.clearTimer(id); const index = this.toasts.findIndex(({ id: _id }) => _id === id); if (index >= 0) { this.toasts = this.toasts.filter(({ id: _id }) => _id !== id); } }; // -weight: 500;">remove a timer private clearTimer(id: string) { if (this._timers.has(id)) { clearTimeout(this._timers.get(id)); this._timers.delete(id); } } COMMAND_BLOCK: // add or -weight: 500;">update a toast item add = (toast: Omit<Toast, "id"> & { id?: string }, duration = 3000) => { const id = toast.id ?? Math.random().toString(36).substring(2, 9); this.clearTimer(id); const newToast = { ...toast, id }; const exists = this.toasts.some((t) => t.id === id); if (exists) { this.toasts = this.toasts.map((t) => (t.id === id ? newToast : t)); } else { this.toasts = [...this.toasts, newToast]; } if (duration > 0) { const timer = window.setTimeout(() => this.-weight: 500;">remove(id), duration); this._timers.set(id, timer); } return id; }; // -weight: 500;">remove a toast and its timer -weight: 500;">remove = (id: string) => { this.clearTimer(id); const index = this.toasts.findIndex(({ id: _id }) => _id === id); if (index >= 0) { this.toasts = this.toasts.filter(({ id: _id }) => _id !== id); } }; // -weight: 500;">remove a timer private clearTimer(id: string) { if (this._timers.has(id)) { clearTimeout(this._timers.get(id)); this._timers.delete(id); } } COMMAND_BLOCK: // add or -weight: 500;">update a toast item add = (toast: Omit<Toast, "id"> & { id?: string }, duration = 3000) => { const id = toast.id ?? Math.random().toString(36).substring(2, 9); this.clearTimer(id); const newToast = { ...toast, id }; const exists = this.toasts.some((t) => t.id === id); if (exists) { this.toasts = this.toasts.map((t) => (t.id === id ? newToast : t)); } else { this.toasts = [...this.toasts, newToast]; } if (duration > 0) { const timer = window.setTimeout(() => this.-weight: 500;">remove(id), duration); this._timers.set(id, timer); } return id; }; // -weight: 500;">remove a toast and its timer -weight: 500;">remove = (id: string) => { this.clearTimer(id); const index = this.toasts.findIndex(({ id: _id }) => _id === id); if (index >= 0) { this.toasts = this.toasts.filter(({ id: _id }) => _id !== id); } }; // -weight: 500;">remove a timer private clearTimer(id: string) { if (this._timers.has(id)) { clearTimeout(this._timers.get(id)); this._timers.delete(id); } } CODE_BLOCK: export const toastManager = new ToastManager(); CODE_BLOCK: export const toastManager = new ToastManager(); CODE_BLOCK: export const toastManager = new ToastManager(); COMMAND_BLOCK: import { toastManager } from '@/singletons/toastManager'; // Add an event listener const subscribe = (callback: () => void) => { const ac = new AbortController(); toastManager.addEventListener("changed", callback, { signal: ac.signal, }); return () => ac.abort(); }; // Get the state const getSnapshot = () => toastManager.toasts; COMMAND_BLOCK: import { toastManager } from '@/singletons/toastManager'; // Add an event listener const subscribe = (callback: () => void) => { const ac = new AbortController(); toastManager.addEventListener("changed", callback, { signal: ac.signal, }); return () => ac.abort(); }; // Get the state const getSnapshot = () => toastManager.toasts; COMMAND_BLOCK: import { toastManager } from '@/singletons/toastManager'; // Add an event listener const subscribe = (callback: () => void) => { const ac = new AbortController(); toastManager.addEventListener("changed", callback, { signal: ac.signal, }); return () => ac.abort(); }; // Get the state const getSnapshot = () => toastManager.toasts; COMMAND_BLOCK: import { useSyncExternalStore } from 'react'; export default function ToastContainer() { const toastList = useSyncExternalStore(subscribe, getSnapshot); return ( <ul> {toastList.map(({id, message}) => (<li key={id}>{message}</li>))} </ul> ); } COMMAND_BLOCK: import { useSyncExternalStore } from 'react'; export default function ToastContainer() { const toastList = useSyncExternalStore(subscribe, getSnapshot); return ( <ul> {toastList.map(({id, message}) => (<li key={id}>{message}</li>))} </ul> ); } COMMAND_BLOCK: import { useSyncExternalStore } from 'react'; export default function ToastContainer() { const toastList = useSyncExternalStore(subscribe, getSnapshot); return ( <ul> {toastList.map(({id, message}) => (<li key={id}>{message}</li>))} </ul> ); } - Location Britain, Europe - Pronouns He/Him - Work Senior Web Developer at bloc-digital - Joined Jun 8, 2017