FirebaseExtended / FirebaseExtended/reactfire
Suspense mode resumes with the previous observable's value on React 19
- Dominant language
- TypeScript
- Stars
- 3.6k
- Forks
- 403
- Avg merge
- 14h 53m
- Merged PRs (30d)
- 5
Description
`useObservable` in suspense mode suspends correctly when the observable and `observableId` are swapped, then resumes rendering the **previous** observable's value.
Found by the first test run ever executed against React 19 (#789); nothing had run against 19 before, only type-checks.
## Reproduction
`test/useObservable.test.tsx`, `emits the new observable's value if the observable is swapped out`. The test renders a component under `React.Suspense`, emits `Jeff` on the first observable, swaps both the observable and the `observableId`, rerenders, then emits `James` on the second.
- **React 18.2.0:** the fallback shows, then the component resumes with `James`. Passes.
- **React 19.2.8:** the fallback shows (that assertion passes), then the component resumes with `Jeff`. Fails.
Deterministic: 3 runs each way, same `@testing-library/react` 16.3.2, only the React runtime swapped via `npm install --no-save react@19 react-dom@19`. No emulators involved, so `npx vitest run useObservable` reproduces it.
## What is known and not known
- The suspend half works. Only the resume is wrong, so this is not the known throw-vs-`use()` question: the 08-07 spike found the `use()` swap is a behavioural no-op on 19 and that React 19 emits no warning for the current throw form. Nothing here contradicts that.
- **The cause is not diagnosed.** The plumbing involved (`SuspenseSubject`, the `preloadedObservables` cache keyed by `observableId`, and the `use-sync-external-store` shim delegating to React's own `useSyncExternalStore` on 19) overlaps what #790 covers, but no connection has been established and it should not be assumed.
## Current state
#789's CI PR skips this test on React 19 only, so the new test job can land green. The skip is a `? it.skip : it` ternary rather than `it.skipIf`, because these globals are typed by `@types/jest` and `skipIf` fails `tsc` while working at runtime.
Restore it to a plain `it` when this is fixed.
Contributor guide
Research direction
Run `npx vitest run useObservable` and start with `test/useObservable.test.tsx`, especially `emits the new observable's value if the observable is swapped out`. Trace the named `SuspenseSubject`, `preloadedObservables` cache, and `use-sync-external-store` path. Done means the test is restored to plain `it` and passes under React 19 while retaining the React 18 behavior.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- react, typescript
- Domain
- frontend
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Quiet
- Clarity
- Mostly clear
- Newbie friendliness
- 48/100