Skip to content
This repository was archived by the owner on Nov 4, 2026. It is now read-only.
This repository was archived by the owner on Nov 4, 2026. It is now read-only.

react-kind pages remount and lose their state on any PageView re-render (ReactKindPage recompiles on schema object identity) #13268

Closed

Description

@objectstack-fleet

Filing gate: ① a user-visible defect with a measured reproduction and a named landing (pm:queue class). Filed by domain:ui seat 2 (session_01XVp19E1MYUTFM3dnkwz8gB) from the os-dev report on PR #13267 (objectui#13238). ⛔ Not a claim.

Reader (who acts): the objectui domain:ui lane.

Measured

  • Where: a react-kind page in the console, the showcase Task Desk page (showcase_task_desk). The console ran on the Vite dev server with StrictMode on, against the objectstack showcase backend at 9d13b5fc8.
  • What happens: when PageView re-renders, the react page is recompiled and remounted, so its React state is lost. On Task Desk, editId resets to null, and an open record drawer disappears with it. objectui#13238's drawer vanished this way: the route's Suspense fallback came and went, PageView re-rendered, and the page remounted.
  • Evidence (temporary logs, since restored): "ReactKindPage scope changed schemaChanged=true", then "Page render editId=null".
  • Mechanism, read from source:
    • PageView passes SchemaRenderer a new spread of the page schema on every render.
    • ReactKindPage's scope useMemo depends on [schema, adapter], so a new schema object recompiles and remounts the page through ReactRunner.
    • The page's code did not change; only the object identity did.
  • Not measured: which re-renders trigger it outside StrictMode in production. objectui#13238's fix (a Suspense boundary in DrawerForm) removed that card's trigger, but any other PageView re-render (a route param, a parent state change) would remount the page the same way.

Expected

A react page keeps its state across a PageView re-render that does not change its source. The page is recompiled only when its compiled inputs change (its code and declared scope), not when the schema object's identity changes.

Landing

  • PageView's inline schema spread: stabilize it, for example by memoizing it on the page's content.
  • And/or ReactKindPage's scope memo: key it on the values it actually compiles from.

The lane chooses. ⛔ Do not weaken StrictMode to hide it.

Pin shape: under StrictMode, a react page with local state survives a forced PageView re-render, with a CONTROL that a real source change still recompiles.

class: a · reach: every react-kind page holding local state; measured on Task Desk.
Dedupe words: react page loses state on rerender; ReactKindPage recompile schema identity; PageView fresh page schema remount. Parent: objectui#13238. No other open card covers it.


Generated by Claude Code

Activity

  1. added
    area:recordsBusiness objects, records, the views that show data, usable forms, search
    bugSomething isn't working
    domain:uiobjectui ui stream: fix lands on the published library or apps — objectui execution seat
    and removed on Oct 20, 2026
  2. objectstack-fleet commented on Oct 20, 2026

    @objectstack-fleet
    ContributorAuthor

    Claim: PM loop round 17 (priority:p2)
    Session: session_01XVp19E1MYUTFM3dnkwz8gB
    Account: baozhoutao (the seat's linked user as GET /user answers it; the card's assignee)
    Branch: claude/issue-13268-react-page-identity
    Worktree: objectui-issue-13268
    Domain: domain:ui
    Seat: domain:ui#2
    File surface (one root cause: a react page recompiles on schema object identity):

    • packages/components/src/renderers/layout/react-page.tsx (ReactKindPage's scope memo and its ReactRunner key);
    • packages/app-shell/src/views/PageView.tsx (the inline schema spread handed to SchemaRenderer), only if stabilizing it there is the narrower fix;
    • tests beside them;
    • one .changeset/13268-react-page-identity.md (patch for each touched package).

    ⛔ No StrictMode change; no new export or prop. Stop on breach; explain in the report.
    Container & model: S, mode:subagent, model: opus (default tier; no path-derived mandate)
    Clause-②: no (internal render identity)
    Responsibility: ReactKindPage recompiles and remounts whenever it receives a new schema object, and PageView hands it a new spread on every render | the page's compiled inputs (its code and declared scope) are what should key a recompile | every react-kind page holding local state (measured: showcase Task Desk lost editId and its open drawer)
    Thread-read: none
    Serial constraints cleared:


    Generated by Claude Code

  3. objectstack-fleet commented on Oct 20, 2026

    @objectstack-fleet
    ContributorAuthor

    os-dev-report

    {
      "issue": 13268,
      "status": "done",
      "branch": "claude/issue-13268-react-page-identity",
      "pr": "https://github.com/objectstack-ai/objectui/pull/13269",
      "session": "session_01XVp19E1MYUTFM3dnkwz8gB",
      "premise_still_valid": true,
      "summary": "Premise confirmed on origin/main 1ca759a16f: ReactKindPage memoised its scope on [schema, adapter] and ReactRunner recompiles on scope identity, so a new but equal schema object remounted the page (pin (a) red on base). Fixed at the ReactKindPage seam only (ruling 2: any host can hand a fresh object; PageView.tsx untouched, StrictMode untouched, no new export or prop): the scope is held in useState and rebuilt during render only when the schema CONTENT (file-local structural sameContent, cycle-safe, non-plain values by identity) or the adapter identity changes; useState rather than useMemo per AGENTS.md #10, rather than a ref because react-hooks/refs flags ref reads in render. A changed source or declared scope value still recompiles and resets the page; measured: scope values are compiled in, so a value change reaches the page only via a recompile (pin c).",
      "tests": "New packages/components/src/__tests__/react-page-content-identity.test.tsx, all under React.StrictMode: (a) state survives two host re-renders with new-but-equal schema objects; (b) CONTROL source change recompiles+resets; (c) CONTROL variables.greeting change recompiles, new value reaches page, state resets; (c2) CONTROL context.params change recompiles. RED on base 1ca759a16f: 'Tests 1 failed | 3 passed (4)' ((a) failed). ABLATION at head c016ba798f via objectstack scripts/ablation-replace.mjs: anchor '!sameContent(compiled.schema, schema)' to 'compiled.schema !== schema', anchor x1 to x0, blob 149963d2927e to 16dddb7d4244, (a) red 'Tests 1 failed | 3 passed (4)'; restore: blob after restore 149963d2927e6dc51a1ff183e1ca24863ab6e077 == HEAD blob, git diff HEAD empty (test imports '../renderers' source, no dist in path). FAMILY at c016ba798f (20 test files git-grep-naming react-page/ReactKindPage across components, console, plugin-charts, plugin-list, sdui-parser, types, core + new file, --maxWorkers=1, under os-verify-lock): 'Test Files 21 passed (21) / Tests 764 passed (764)', VERDICT command-exit 0. Closure build pnpm --filter '@object-ui/components^...' build exit 0; pnpm --filter @object-ui/components type-check exit 0 at c016ba798f (tsconfig.test.json --listFiles includes the new test). eslint --no-inline-config --format json on 3 changed TS files: files 3, errors 0; react-page.tsx warnings equal base's six no-explicit-any minus base's react-hooks/exhaustive-deps. Declared to CI: full packages/components/ directory.",
      "gates": {
        "type-check @object-ui/components": "exit 0 @ c016ba798f",
        "vitest family (21 files)": "exit 0 @ c016ba798f",
        "eslint changed files": "exit 0, 0 errors",
        "check:phantom-deps": 0, "check:self-import": 0, "check:unreferenced-sources": 0, "check:doc-example-readers": 0,
        "check:handler-key-reads": 0, "check:side-effects-array": 0, "check:element-data-source-declaration": 0, "check:spec-symbols": 0,
        "check:action-forward-parity": 0, "check:designer-field-key-parity": 0, "check:icon-record-names": 0, "check:i18n-keys": 0,
        "check:i18n-drift": 0, "check:i18n-designer-parity": 0, "check:control-bytes": 0,
        "check-hand-rolled-comment-mask.mjs": 0, "check-changeset-fixed.mjs": 0, "check-changeset-presence.mjs": 0, "check:new-line-citations": 0,
        "check:spec-floors": "exit 1 — NOT MEASURED: 'no-artifact: Build the workspace before running this gate' (no full workspace build run)",
        "CI": "in_progress — not awaited"
      },
      "files_changed": [
        "packages/components/src/renderers/layout/react-page.tsx",
        "packages/components/src/__tests__/react-page-content-identity.test.tsx",
        "packages/components/src/__tests__/react-page-state.test.tsx",
        ".changeset/13268-react-page-identity.md"
      ],
      "line_budget": "not applicable (no skills/** or governed surface touched)",
      "deviations": [
        "react-page-state.test.tsx edited only in its header comment (it described the old [schema, adapter] memo); within the claim's 'tests beside them'.",
        "Labels: zero write (objectui, dispatch named none)."
      ],
      "mcp_calls": "0",
      "api_writes": "2 — fleet-write relay: (1) pr_create objectstack-ai/objectui (POST /repos/objectstack-ai/objectui/pulls draft + POST issues/13269/assignees), (2) this os-dev-report comment POST /repos/objectstack-ai/objectui/issues/13268/comments; plus 3 git pushes of the branch",
      "open_questions": [],
      "out_of_scope_findings": [
        "carrier: 承接者:无 · PageView still hands SchemaRenderer a fresh page spread each render; now harmless to react pages, effect on other blocks unmeasured · noted in PR Acceptance notes, not filed"
      ]
    }

    Generated by Claude Code

  4. objectstack-fleet commented on Oct 20, 2026

    @objectstack-fleet
    ContributorAuthor

    ACCEPT — PR #13269 @ c016ba798fc7fde63f58b2885e9202c3d9d29219 · domain:ui seat 2 (session_01XVp19E1MYUTFM3dnkwz8gB)

    • Clause-②: no. An internal render-identity fix; PageView.tsx and StrictMode are untouched; no export or prop moves. No contract review is owed, and the seat read the report.
    • Seat review: the fix is at the ReactKindPage seam (ruling 2's preference, since any host can hand it a fresh object). The compiled scope is held in useState and rebuilt during render only when the schema's content changes (a file-local structural sameContent, cycle-safe, with non-plain values compared by identity) or the adapter identity changes. A new but equal schema object no longer recompiles or remounts the page. useState follows AGENTS.md [WIP] Enhance every detail of the designer #10; a ref was ruled out because react-hooks/refs flags ref reads in render.
    • Measured fact pinned: scope values are compiled in, so a changed declared value reaches the page only through a recompile, which resets its state. That is correct, and pin (c) holds it.
    • Pins (all under StrictMode): (a) state survives two host re-renders with new but equal schema objects, and is red on base 1ca759a16f; (b) CONTROL: a source change recompiles and resets; (c) and (c2) CONTROL: a variables or context.params change recompiles and the new value arrives. The ablation (back to identity comparison) turned (a) red, and the restore was proven by blob. The react-page family (21 files, 764 tests) is green.
    • Changeset: @object-ui/components patch.
    • CI: all 36 check-runs completed on the head, 33 passed and 3 skipped.
    • Path fork: no governed path. Seat landing.
    • Findings: Acceptance notes. PageView still hands SchemaRenderer a fresh page spread on each render. That is now harmless to react pages; its effect on other blocks is unmeasured, so it is not filed.

    Next: ready + auto-merge; landing is verified by content on main.


    Generated by Claude Code

  5. objectstack-fleet commented on Oct 20, 2026

    @objectstack-fleet
    ContributorAuthor

    Landed · domain:ui seat 2 · session_01XVp19E1MYUTFM3dnkwz8gB

    • Merged: PR fix(components): recompile a react page on its content, not its schema identity #13269 through the merge queue as main 8cd4db4974.
    • Verified by content: each of the 4 files the PR changes has the same blob at 8cd4db4974 as at the accepted head. The merge message carries no model identifier.
    • Landed: a react-kind page recompiles only when its schema's content or its adapter changes. A host re-render that hands it a new but equal schema object keeps the mounted page and its state, under StrictMode too. A real source or scope change still recompiles.

    pm:dispatched is removed.


    Generated by Claude Code

  6. added a commit that references this issue on Oct 28, 2026
    8cd4db4
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

area:recordsBusiness objects, records, the views that show data, usable forms, searchbugSomething isn't workingdomain:uiobjectui ui stream: fix lands on the published library or apps — objectui execution seatpriority:p2

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions