Skip to content

Studio Data pillar ignores its renderListView slot's refreshKey — the prop was forwarded to a component that never declared it #4549

Description

@yinlianghui

Surfaced by the repo-wide type-check canary in #4528 (PR pending), which is the first thing ever to check ListView's call sites. Filed rather than fixed there: #4528 is a type-only card and wiring this is a behaviour change.

What was measured

packages/app-shell/src/views/studio-design/StudioDesignSurface.tsx renders the Data pillar's grid through the plugin ObjectView's renderListView slot. That slot supplies a refreshKey (declared at packages/plugin-view/src/ObjectView.tsx:207), and renderStudioGridList forwarded it straight on:

<ListView
  schema={...}
  dataSource={ds as never}
  onEdit={onEdit}
  onAddRecord={onAddRecord}
  className={className}
  refreshKey={refreshKey}
/>

refreshKey is not a prop of ListView, and it is not a prop of anything ListView renders. ListView keeps its own refreshKey local state (ListView.tsx:831) and drives refetching off schema.refreshTrigger plus that state; ObjectGrid likewise keeps its own local refreshKey state and declares no such prop. The incoming value rode ListView's {...props} forward into SchemaRenderer and was dropped.

Grepping the whole repo for refreshKey?: as a prop declaration returns plugin-view/ObjectView.tsx (the slot contract), StudioDesignSurface itself, and four unrelated components — no view component in the forward chain.

It type-checked only because ListViewProps carried a [key: string]: any that erased the whole interface (#4528), so no ListView call site was checked at all.

Why it was invisible

Bumping the prop still re-renders ListView (new prop object), and the Studio grid re-renders constantly anyway because its schema is a fresh object literal on every render. So "the list looks like it updates" — it just never refetches because of refreshKey.

The working precedent, 300 lines away

packages/app-shell/src/views/ObjectView.tsx:1592 consumes the same slot value correctly — it folds it into React's key so the subtree remounts:

const combinedRefreshKey = refreshKey + (pluginRefreshKey || 0);
const key = `${objectName}-${activeView.id}-${combinedRefreshKey}`;

Current state

#4528's PR removes the dead prop (behaviour-preserving) and leaves a comment at the declaration pointing here. So today the Studio Data pillar has no refreshKey path at all, which is what it effectively had before — the difference is that it is now visible.

Worth deciding

Whether the Studio Data pillar needs the slot refresh at all. If it does, the key fold above is the established shape. If it does not, the slot prop can be dropped from renderStudioGridList's signature entirely. Someone who knows what user action is meant to trigger it (record create / delete / publish?) should judge, since "remount the grid" also discards scroll position, selection and in-progress inline edits — AGENTS.md #8's "refresh data, don't rebuild UI" argues against the key fold as the long-term answer even though it is what the sibling does.

Refs #4528, #1126.


Generated by Claude Code

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