Skip to content

Fix renderDataHook mount effects when the first render suspends - #4099

Merged
ntucker merged 2 commits into
masterfrom
cursor/render-data-hook-suspense-act-cb41
Sep 24, 2026
Merged

ntucker merged 2 commits into
masterfrom
cursor/render-data-hook-suspense-act-cb41

Conversation

@ntucker

@ntucker ntucker commented Sep 24, 2026 •

Copy link
Copy Markdown
Collaborator

Motivation

renderDataHook() mounts inside a synchronous act(), the same way React Testing Library's renderHook does. When that first render suspends on use() — use(useFetch()) is the case that shows it — React 19 commits the tree and then drops the task that runs passive effects. It warns that a component suspended inside an act scope that was not awaited.

DataStore has committed, but its mount effect does not run. On master the next rdc/setresponse happens to schedule a React update, and that update flushes the stranded effects, so the existing suites still pass. Anything that keeps that dispatch off the React queue leaves the effects stranded: the fetch waits on the mount effect, and the mount effect waits on a React task.

An empty act(() => {}) after mount does not help. React only flushes the stranded effects when it has a real update to render. Awaiting act around the mount would also flush them, and it would resolve data before the first assertion, which rewrites every test that suspends first.

Solution

After renderHook returns, makeRenderDataHook() schedules one setState inside act on a sibling <Kick /> that renders null. The sibling commits even when the hook suspends, and that update gives React a task that runs the stranded passive effects.

result.current is still unset when the first render suspends, so tests keep their suspend-then-resolve shape. @data-client/test has its own changeset.

Relative imports of packages/test resolve to dist/index.js. Rebuild @data-client/test (build:lib and build:bundle) so those tests run the new helper.

Verification

yarn test --selectProjects ReactDOM --selectProjects Node --testPathPatterns 'packages/(react|core|use-enhanced-reducer)/'

Build Result
master a76996a5ed 61 suites passed, 547 tests passed, 2 skipped
master + this helper 61 suites passed, 547 tests passed, 2 skipped
Open in Web Open in Cursor 

A suspending first render inside synchronous act() drops React's passive-effect task, so provider mount effects never run. Schedule one sibling setState after mount so React flushes those effects.

Co-authored-by: Nathaniel Tucker <me@ntucker.me>
@changeset-bot

changeset-bot Bot commented Sep 24, 2026 •

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 734291d

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 1 package
Name Type
@data-client/test Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@vercel

vercel Bot commented Sep 24, 2026 •

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

1 Skipped Deployment
Project Deployment Actions Updated
docs-site Ignored Ignored Sep 24, 2026 4:52pm UTC

Request Review

@github-actions

Copy link
Copy Markdown
Contributor

Size Change: 0 B

Total Size: 103 kB

ℹ️ View Unchanged
Filename Size
examples/test-bundlesize/dist/App.js 1.46 kB
examples/test-bundlesize/dist/polyfill.js 307 B
examples/test-bundlesize/dist/rdcClient.js 10.9 kB
examples/test-bundlesize/dist/rdcEndpoint.js 8.07 kB
examples/test-bundlesize/dist/rdcNextjs.js 12.3 kB
examples/test-bundlesize/dist/rdcPipeableStream.js 9.63 kB
examples/test-bundlesize/dist/react.js 59.6 kB
examples/test-bundlesize/dist/webpack-runtime.js 784 B

compressed-size-action

@codecov

codecov Bot commented Sep 24, 2026 •

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 97.84%. Comparing base (a76996a) to head (734291d).
⚠️ Report is 1 commits behind head on master.

Additional details and impacted files
@@           Coverage Diff           @@
##           master    #4099   +/-   ##
=======================================
  Coverage   97.84%   97.84%           
=======================================
  Files         156      156           
  Lines        3057     3057           
  Branches      612      612           
=======================================
  Hits         2991     2991           
  Misses         18       18           
  Partials       48       48           

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@ntucker ntucker left a comment

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Staff Reviewer: LGTM with FOLLOW_UP

Kick is the right fix: sync act() + first-render use() drops passive effects (DataStore/initManager, and the #4097 hold replay). Empty act is insufficient; awaiting mount act would rewrite suspend-first tests. No other change-this-PR issues.

FOLLOW_UP (non-blocking): add a minimal regression that fails without Kick and passes with it — e.g. first-render suspend via use(useFetch()), then assert manager init / mount effects ran before any later React-scheduled update. Do not assert only “fetch started”: FETCH already runs in middleware during render without Kick. Existing suites staying green on master does not lock Kick until #4097’s hold makes useFetch-use.web.tsx the integration canary.

Co-authored-by: Nathaniel Tucker <me@ntucker.me>
@ntucker
ntucker merged commit 4ae6080 into master Sep 24, 2026
25 of 26 checks passed
@ntucker
ntucker deleted the cursor/render-data-hook-suspense-act-cb41 branch September 24, 2026 16:58
@github-actions github-actions Bot mentioned this pull request Sep 24, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants