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.

Studio Data pillar grid issues a duplicate find() on every render — the columns array identity is in ListView's fetch dependency chain #4567

Closed

Description

@yinlianghui

Measured while verifying #4549 (the slot refreshKey card). Filed separately: it is upstream of that slot and orthogonal to it.

What was measured

Driving renderStudioGridList (the Data pillar's renderListView slot) with a real ListView and a counting dataSource, then forcing three re-renders that change nothing — same refresh signal value, same column names:

t=600ms find=1              (mount settled)
--- 3 rerenders (same counter) ---
t+50ms find=4

One extra find() per render. Re-running the identical harness with the columns array hoisted to a stable identity and changing nothing else:

t=600ms find=1
--- 3 rerenders (same counter) ---
t+300ms find=1
--- bump ---
t++50ms find=2

Zero extra fetches on re-render, and the refresh signal still refetches exactly once. So the trigger is the identity of schema.columns, not its contents.

Why it fires in Studio

ListView derives its expand fields from schema.columns, and that derivation sits in the fetch effect's dependency array by identity (packages/plugin-list/src/ListView.tsx:1609 — expandFields among the deps). A fresh array literal per render therefore re-runs the fetch.

The Studio Data pillar hands it exactly that. packages/app-shell/src/views/studio-design/StudioDesignSurface.tsx:2600 builds the object-view schema inline:

table: {
  fields: readFields(objDraft.fields).entries.map(...).filter(...),
},

.map().filter() allocates a new array on every render, plugin-view's ObjectView forwards it to the slot as columns (packages/plugin-view/src/ObjectView.tsx, the renderListView payload), and the slot spreads it into ListView's schema. The Studio grid re-renders constantly (its whole schema is a fresh object literal each time), so this is a steady-state duplicate-query source against the backend, not a rare edge.

Impact

Every keystroke-level re-render of the Data pillar issues another list query. Invisible in the UI — the rows just repaint with the same data — so it reads as "working" while multiplying backend load.

Worth deciding

Two candidate fixes, opposite ends:

  1. Producer side — memoize the table.fields array in StudioDesignSurface (React.useMemo keyed on the draft's field names). Narrow, but only fixes this one consumer; any other caller passing a fresh array has the same defect.
  2. Consumer side — have ListView depend on the column names' value (a joined key) rather than the array identity. Fixes every caller at once, but it is a plugin-list change and needs care that a genuinely changed column set still refetches.

(2) looks like the real fix, since the identity contract is currently unstated and every caller has to guess it. Someone who owns plugin-list's fetch effect should judge.

Refs #4549, #4528.


Generated by Claude Code

Activity

  1. self-assigned this
    on Aug 13, 2026
  2. yinlianghui commented on Aug 13, 2026

    @yinlianghui
    ContributorAuthor

    CLAIM — session_017Qqyix2QcnpUC9XeYVDzx3, branch claude/issue-4567-studio-grid-duplicate-find. Dispatching a dev agent now.

    PM ruling (delegated decision authority; maintainer veto window open — record objections here): the filer's own measurement is the spec:

    1. Hoist StudioDesignSurface's table.fields rebuild (~:2600, .map().filter() per render) to a stable identity — useMemo keyed on the actual inputs (measure what they are: the object's field list + whatever filters apply), so the columns array changes identity only when its content changes. The fix is identity stabilization at the PRODUCER; do not paper over it by loosening ListView's dependency semantics (its by-identity dep is correct for real column changes — read-only).
    2. Red-first with the filer's own measurement shape: three no-op re-renders currently produce extra find() calls (1→4 measured); post-fix zero extra, while a REAL column change (add/remove a field) still refetches (pin — the dep must stay live), and chore(app-shell): the dead slot refreshKey parameter is removed (#4549) #4571's bump→refetch and no-storm pins stay green untouched.
    3. Changeset: '@object-ui/app-shell' patch (module-local memoization; verify entry .d.ts byte-identical both ways). Never major.

    Sequencing: gate worktree creation on PR #4571 reaching origin/main (armed on green CI — same file, same region family; its gridRefresh test is your harness pattern and its columns-hoisting workaround in test 2 is the thing your fix makes unnecessary — leave that test as-is unless it conflicts, then declare). Mutual exclusion: ⛔ #4548 (packages/react + plugin-dashboard DashboardRenderer/GridLayout), #4305 (app-shell FLOW files), #4566 (core dataset-format + DatasetWidget/metric) — disjoint; StudioDesignSurface's #4558-landed wiring untouched.


    Generated by Claude Code


    Generated by Claude Code

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions