Uh oh!
There was an error while loading. Please reload this page.
fix(sdui): stop the react page's "no adapter yet" fallback churning its provider context - #3000
Merged
Merged
Conversation
…ts provider context Audit of the remaining half of ReactKindPage's scope memo, [schema, adapter]. The schema half was the live bug fixed in #2984; this is the adapter half. The hosts are fine. Both AdapterCtx.Provider call sites pass a stable value — AdapterProvider from useState, the console preview from a module constant — so there is no state loss in the shipped app. One real instance remained, one layer down: `<SchemaRendererProvider dataSource={adapter ?? {}}>` minted a fresh object every render while the adapter was still null (the window before the host connects). That is a context value and SchemaRendererProvider memoises on its identity, so every block inside the page had its schema re-cloned and its expressions re-run on each render of the page. Now a module constant, mirroring the SchemaRenderer fallback. The `adapter` dependency itself must stay, and is now pinned. It looks like the obvious thing to optimise away — it is the last remaining trigger that can recompile a page and cost its useState. But ReactRunner hands React the same element object while (code, scope) hold, and React bails out on an identical element reference, so the page subtree never re-renders on its own: recompiling is the ONLY path by which a new adapter reaches the blocks inside the page. Verified by removing the dependency — blocks stay pinned to the first adapter forever, with no error, just a dead data source. react-page-adapter.test.tsx pins both directions so the tradeoff cannot be quietly re-litigated. Docs: the react-pages guide now states the host-side requirement — an adapter constructed inline on every render resets every react page on every render. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01N4mrr1ihhwnfEHFSWmGoMp
The latest updates on your projects. Learn more about Vercel for GitHub. |
Contributor
✅ Console Performance Budget
📦 Bundle Size Report
Size Limits
|
os-zhuang
marked this pull request as ready for review
July 30, 2026 08:43
Uh oh!
There was an error while loading. Please reload this page.
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
Sign up for freeto join this conversation on GitHub.
Already have an account?
Sign in to comment
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.
Audit of the remaining half of
ReactKindPage's scope memo,[schema, adapter]. Theschemahalf was the live bug fixed in #2984; this is theadapterhalf.Headline: the hosts are fine. Both
AdapterCtx.Providercall sites pass a stable value —AdapterProviderfromuseState, the console preview from a module constant. There is no state loss in the shipped app. This PR is one small real fix plus the guard that the audit turned out to justify.1. One real instance, one layer down
adapter ?? {}mints a fresh object on every render while the adapter is still null — the window before the host finishes connecting. That is a context value, andSchemaRendererProvidermemoises on its identity, so every block inside the page had its schema re-cloned and its expressions re-run on each render of the page. Same class as #2984'sNO_DATA_SOURCE, now the same fix.2. The
adapterdependency must stay — and is now pinnedThis is the interesting part. That dependency looks like the obvious thing to optimise away: it is the last remaining trigger that can recompile a page and cost its
useState, and "read the adapter through a ref so the scope stays stable" is a natural-sounding improvement. I started to make that change.It is wrong, and quietly so.
ReactRunnerhands React the same element object while(code, scope)hold, and React bails out of re-rendering a child whose element is referentially identical. The page subtree therefore never re-renders on its own — recompiling is the only path by which a new adapter reaches the blocks inside the page.Verified rather than reasoned about. Dropping the dependency:
Every block stays pinned to the first adapter forever. No error, no warning — just a data source that quietly stopped being the live one.
react-page-adapter.test.tsxpins both directions so the tradeoff cannot be re-litigated by accident:3. Docs
The guide's "Page state" section listed two things that reset a page. There are three, and the third is a requirement on the host, not the author: an adapter constructed inline on every render resets every react page on every render. Now spelled out with the correct and incorrect shapes, noting
AdapterProvideralready does it right — the rule matters for custom hosts and preview surfaces.Verification
702 files passed | 1 skipped,8237 tests passed | 24 skipped.lint+type-checkfor@object-ui/components: clean, 0 errors.check-doc-links: the guide's links resolve. (The one pre-existing break incontent/docs/core/enhanced-actions.mdxis untouched and not gating.)changeset:check: clean. Changeset included.🤖 Generated with Claude Code
https://claude.ai/code/session_01N4mrr1ihhwnfEHFSWmGoMp
Generated by Claude Code