You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
feat: Use onPostCommit to ensure that render/rerender and takeNext promises resolve after effects are run - #18
As reported in #17, since React 19.1, passive effects (useEffect, and the subscription in useSyncExternalStore) can run in a later task than their commit.
That means that with the current approach, render/rerender or even takeNext could trigger before all effects for that render had finished processing.
This switches over to onPostCommit, an api that is not part of the official React types, but has been around stable since React 18, based on this React RFC and implemented in react/react#17910.
phryneas
changed the title
Use onPostCommit to ensure that render/rerender and takeNext promises resolve after effects are run
feat: Use onPostCommit to ensure that render/rerender and takeNext promises resolve after effects are run
Oct 27, 2026
This PR updates render-stream synchronization to wait for passive effects, addressing effect timing changes in React 19.1 while retaining a legacy fallback for React 16/17.
Changes:
Use Profiler onPostCommit to release render and snapshot consumers after effects run.
Add regression tests for subscriptions, overlapping rerenders, skipped commits, and snapshot errors.
Update API documentation, legacy React DOM typing, and the Symbol.dispose polyfill.
File
Description
tests/polyfill.js
Preserve an existing native Symbol.dispose.
src/renderWithoutAct.tsx
Type legacy React DOM APIs locally for newer type definitions.
src/renderStream/createRenderStream.tsx
Track effect completion and delay render consumers accordingly.
The new regressions run only against React 19.0.0: package.json pins both React packages to that version, and .github/workflows/ci.yml has no version matrix. This leaves the React 19.1+ delayed-passive-effect behavior motivating this change, and the new React 16/17 fallback, untested in CI. Please add coverage on at least one React 19.1+ version and a legacy version so regressions in these scheduling paths cannot pass validation unnoticed.
Use unique tokens for legacy commits sharing the same timestamp
src/renderStream/createRenderStream.tsx:254
With a coarse or mocked clock, multiple React 16/17 commits from the same root can share a commitTime and overwrite their pending entries. For interleaved commits A1, B1, A2 from two roots with the same timestamp, A1's timer advances effectsFlushedCount to 3, then B1's timer reads past the recorded renders and throws when accessing phase. Give each legacy fallback a unique per-record token so pending commits cannot overwrite one another.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
As reported in #17, since React 19.1, passive effects (useEffect, and the subscription in useSyncExternalStore) can run in a later task than their commit.
That means that with the current approach,
render/rerenderor eventakeNextcould trigger before all effects for that render had finished processing.This switches over to
onPostCommit, an api that is not part of the official React types, but has been around stable since React 18, based on this React RFC and implemented in react/react#17910.React 16.8+ and 17 retain existing behaviour.