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.

[finding] RecordContextValue 的 loading / error 两个字段全仓零生产者、零消费者(refresh 的同班同学,已在 #3460 补上生产者) #3773

Closed

Description

@yinlianghui

观察类发现,当前无用户可见影响,记录备查,不建议插队。发现于 #3460 的实现过程(该单给同一 interface 的 refresh 补上了生产者与消费者)。

事实

packages/react/src/context/RecordContext.tsx 的 RecordContextValue 声明了三个"由宿主填充"的状态字段:

/** True while the record is fetching. */
loading?: boolean;
/** Last fetch error, if any. */
error?: Error | null;
/** Re-fetch the record from the source. */
refresh?: () => void | Promise< void >;

(注:上面 Promise< void > 的空格是为了绕开 GitHub 正文把 < + 字母当成 HTML 标签吞掉,源码里没有这个空格。)

三者都进了 RecordContextProvider 的 useMemo 依赖数组,类型、lint、门禁全绿。但实读当前 main:

为什么现在没人踩到

没有消费者,就没有"读到 undefined 的错误分支";没有生产者,就没有"写了却没人读"的浪费。这是休眠声明,不是缺陷 —— 用户今天碰不到。

值得记一笔,是因为这正是本仓反复点名的 declared ≠ enforced 形状:字段声明了、注释写清了语义、类型与 lint 全绿,而没有任何代码路径写它、也没有任何代码路径读它。#3460 的分诊评论把 refresh 当作这个形状的教科书例子,而它在同一个 interface 里还有两个同班同学。

一个具体的次生风险(仍属观察):RecordDetailView 的记录加载 effect 里已有 pageRecordStatus === 'loading' 这一现成状态,把它接进 loading 是一行的事 —— 但那会让 context 每次取数多一次失效(loading 在 memo 依赖数组里),从而让所有 record:* 消费者多一轮重渲染。也就是说"顺手补上生产者"并非零成本,值得先定"这个字段到底服务谁"再动。#3460 因此没有顺手补:与其新增一个只有那个刷新按钮会读的半截语义,不如把它留成一条可评审的记录。

可能的处置(留给维护者裁决,本单不主张任何一种)

  1. 补齐:RecordDetailView 生产 loading / error,并明确哪个渲染器该读它们(骨架屏?错误条?)—— 需要先有消费方需求,否则又是一次 declared ≠ enforced。
  2. 退休:按 enforce-or-remove 删掉两个字段(以及 memo 依赖数组里的对应项),让"记录级 loading/error"这件事只由各渲染器自己的数据源表达。
  3. 维持现状并接受它们是给宿主预留的扩展点 —— 那么至少该在注释里写明"当前无生产者",免得下一个实现者以为读它就能拿到值(feat(record-detail): 记录详情页头部增加手动刷新按钮(不 reload 浏览器即可刷新详情/相关/计数) #3460 差点这么做)。

查重

已按关键词(RecordContext、RecordContextValue、loading 生产者)搜过本仓 open issue,无影子单;#3460 是相邻单但主题是刷新按钮本身,不覆盖这两个字段。

Activity

  1. os-zhuang commented on Aug 9, 2026

    @os-zhuang
    Contributor

    Findings-round grading: hold — finding stays.

    • Why still held: the card's three dispositions (produce the fields / retire them per enforce-or-remove / keep as a documented extension point) need a "who does this field serve" answer first, and the card correctly measures that the cheap-looking fix is not free — wiring pageRecordStatus into loading adds a context invalidation per fetch that re-renders every record:* consumer. Dormant declaration, zero user impact today, so it waits for that ruling rather than the queue.
    • Premise re-check on origin/main @ 50fa376: packages/react/src/context/RecordContext.tsx:124 still declares loading?: boolean (with error beside it); no producer/consumer has appeared since filing.
    • Exit condition: a maintainer ruling on any of the three directions promotes this to an XS card (option 3 — a "currently no producer" comment at the declaration — is the cheapest honest floor if no consumer need materializes).

    本评论来自分诊座位 Routine(#5474 试点),不构成认领。


    Generated by Claude Code

  2. claude commented on Aug 10, 2026

    @claude
    Contributor

    Findings round (periodic re-verification of oldest-checked holds): HOLD maintained (finding unchanged). Re-checked @ origin/main 6b6721c: packages/react/src/context/RecordContext.tsx:124 still declares loading?: boolean and error?: Error | null beside it, both with zero producers/consumers. The who-does-this-serve ruling has not arrived; the cheap-looking wire-up still carries the per-fetch invalidation cost the card measured. Held.

    本评论来自分诊座位 Routine(#5474 试点),不构成认领。


    Generated by Claude Code

  3. claude commented on Aug 10, 2026

    @claude
    Contributor

    Findings round (quota re-verification): HOLD maintained (finding unchanged). Re-checked @ origin/main 2b94283: packages/react/src/context/RecordContext.tsx:124/:126 still declare loading?: boolean and error?: Error | null with zero producers and zero consumers repo-wide (refresh, the classmate #3460 wired, remains the only enforced member). The who-does-this-serve ruling has not arrived, and the wire-up still carries the measured per-fetch memo-invalidation cost. Enforce-or-retire fork unpicked; held.

    本评论来自分诊座位 Routine(#5474 试点),不构成认领。


    Generated by Claude Code

  4. os-zhuang commented on Aug 11, 2026

    @os-zhuang
    Contributor

    Findings cadence re-check @ origin/main 6314e87: HOLD — finding stands.

    Premise live: packages/react/src/context/RecordContext.tsx:112 interface still declares loading? and error? (with refresh? beside them, which #3460 gave a producer); no producer/consumer has appeared for the two fields. Dormant declared surface — held pending either a wiring change that needs them or a retirement sweep in this family.

    本评论来自分诊座位 Routine(#5474 试点),不构成认领。


    Generated by Claude Code

  5. os-zhuang commented on Aug 11, 2026

    @os-zhuang
    Contributor

    Findings triage (cadence re-check): HOLD — stays finding.

    Premise re-verified on origin/main @ 6d8231c: packages/react/src/context/RecordContext.tsx:124/:126 still declare loading? / error?, and no producer or consumer has appeared (refresh, the classmate #3460 wired, remains the only live one of the three).

    Why still held: dormant declaration, zero user impact in either direction. The clean resolutions are still the same pair — wire producers when a real consumer arrives, or remove the two fields as enforce-or-remove hygiene next time RecordContextValue's public shape is open — and neither is worth a standalone dispatch today.

    本评论来自分诊座位 Routine(#5474 试点),不构成认领。


    Generated by Claude Code

  6. os-zhuang commented on Aug 11, 2026

    @os-zhuang
    Contributor

    Findings triage: HOLD — finding unchanged. Considered for the dead-surface sweep #4328 this round and excluded on premise drift: on origin/main @ b16be40, RecordContextValue (packages/react/src/context/RecordContext.tsx:112) has grown several fields since filing (embedded, headerSystemActions, isFavorite, …), the provider now spreads its entire props object into the context value, and the memo dep-list explicitly includes value.loading / value.error — so "zero producers / zero consumers" needs re-measurement against the current hosts (a host passing loading through the spread would be invisible to the original grep shape) before deletion is safe. Why still held rather than closed: the two fields remain undocumented-by-usage and the original hazard (declared surface nobody demonstrably serves) is unrefuted — it is the evidence, not the concern, that has gone stale. Restart condition: the #4328 sweep's dev (or any session already inside packages/react) re-measures producers/consumers of both fields at its ref and either files the deletion as a follow-up or closes this with the measurement.

    本评论来自分诊座位 Routine(#5474 试点),不构成认领。


    Generated by Claude Code

  7. os-zhuang commented on Aug 11, 2026

    @os-zhuang
    Contributor

    Findings-triage route B: promoted — finding → pm:queue — completing the re-measurement the 14:47Z round left on record when it excluded this card from sweep #4328.

    Re-measured on origin/main @ f046f88 (the drift noted last round does not change the verdict):

    • Declaration: loading? / error? still on RecordContextValue (packages/react/src/context/RecordContext.tsx:124–126), and named in the provider's memo dep list (:179–180) — the memo-dep mention is the "drift" flagged last round; it is dead wiring, not a producer or consumer.
    • Producers: zero. The live runtime provider call (packages/app-shell/src/views/RecordDetailView.tsx:2054–2065) passes objectName/recordId/data/objectSchema/dataSource/embedded/refresh/headerSystemActions/isFavorite/onToggleFavorite — neither loading nor error. PagePreview's mirror call likewise.
    • Consumers: zero. No ctx.loading / ctx.error read of RecordContext exists in packages/ or apps/; the single textual hit (MetadataProvider.tsx:730) is the Metadata context, a different provider.

    Same class as sweep #4328's members (declared surface, zero producers, zero consumers, refresh — the #3460 classmate — being the one that got a producer instead). Recommend folding into #4328 as its fourth member (noting there); if the sweep is claimed before this lands on it, it stays an independent one-line deletion card.

    本评论来自分诊座位 Routine(#5474 试点),不构成认领。


    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

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions