ExamplesSuspense data fetching
Suspense data fetching
useObservablePromise returns a promise for React’s use(). The Suspense
fallback shows until the first emission; later stream updates do not re-trigger
it.
import {Suspense, use, useState} from 'react' import {useObservablePromise, type ObservablePromise} from 'react-rx' import {map, type Observable, timer} from 'rxjs' const LATENCY_MS = 800 const userCache = new Map<string, Observable<{id: string; name: string; bio: string}>>() /** Stable per id so Suspense retries and remounts share one in-flight request. */ function fetchUser$(id: string) { let observable = userCache.get(id) if (!observable) { observable = timer(LATENCY_MS).pipe( map(() => ({ id, name: id === 'alpha' ? 'Ada Lovelace' : 'Grace Hopper', bio: `Profile loaded for ${id}`, })), ) userCache.set(id, observable) } return observable } const clock$ = timer(0, 1000).pipe( map((n) => `Tick ${n} — live updates skip the Suspense fallback`), ) function Profile({ promise, }: { promise: ObservablePromise<{id: string; name: string; bio: string}> }) { const user = use(promise) return ( <div> <h3>{user.name}</h3> <p>{user.bio}</p> </div> ) } function LiveClock() { const label = use(useObservablePromise(clock$)) return <p style={{fontSize: 14, opacity: 0.8}}>{label}</p> } export default function SuspenseExample() { const [id, setId] = useState('alpha') // Create the promise in a parent that does not suspend, so Suspense retries // always see the same promise identity (see React's use() caching guidance). const promise = useObservablePromise(fetchUser$(id)) return ( <div style={{fontFamily: 'system-ui', padding: 16}}> <p> <button type="button" onClick={() => setId('alpha')}> alpha </button>{' '} <button type="button" onClick={() => setId('beta')}> beta </button> </p> <Suspense key={id} fallback={<p style={{opacity: 0.7}}>Loading {id}…</p>} > <Profile promise={promise} /> </Suspense> <hr style={{margin: '16px 0'}} /> <Suspense fallback={null}> <LiveClock /> </Suspense> <p style={{marginTop: 16, fontSize: 14, opacity: 0.75}}> Switching profiles re-suspends (new observable). The clock updates continuously without flashing a fallback. </p> </div> ) }
Last updated on