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.

SchemaRenderer 的 disabled 门用第三种拼法 !== undefined:disabled: '' / disabled: null 在通用渲染路径上永久置灰 #3862

Closed

Description

@yinlianghui

来源

发现自 #3849(PR #3861)实施时对第三种拼法的逐处核对。#3849 的正文把它列为「实施时请按落点逐一确认」的注意事项,但它不在 #3849 的文件面(#3849 只动五个渲染器落点),也不在 #3850 的修法面(见下「与 #3850 的关系」),故独立成单,未认领。

机理

「是否声明了 disabled 门」在仓里第三次被回答,这次的拼法是 !== undefined,范围比 != null 还要宽一格。

packages/react/src/SchemaRenderer.tsx:322-334(origin/main @ 993336f7c481e1fd724512363b8b2f15b305da86):

// Evaluate disabled: disabled / disabledOn
const isDisabled = (() => {
  if (newSchema.disabled !== undefined) {
    return evaluator.evaluateCondition(newSchema.disabled);
  }
  if (newSchema.disabledOn !== undefined) {
    return evaluator.evaluateCondition(newSchema.disabledOn);
  }
  return false;
})();

if (isDisabled) {
  newSchema._disabled = true;
}

ExpressionEvaluator.evaluateCondition(packages/core/src/evaluator/ExpressionEvaluator.ts:238-240)对空谓词的答复是「没有条件 → 可见/可用」:

// No condition → default to visible/enabled (undefined, null, '').
if (!condition) {
  return true;
}

于是链条闭合:disabled: '' 通过 !== undefined 这一关 → evaluateCondition('') 返回 true → _disabled = true。方向不对称与 #3842 / #3849 完全相同 —— 同一个 true 在 visible 上意味着「显示」(该侧 visible !== undefined 因此良性,SchemaRenderer.tsx:287 的 !evaluateCondition('') = !true = 不隐藏),在 disabled 上意味着「禁用」。

比 != null 家族多亏一格:disabled: null 也会置灰(null !== undefined 为真 → evaluateCondition(null) 为 true)。!= null 的五处落点至少把 null 排在门外。

值不是死的:它会被转发成组件 prop

这一点是 #3849 正文没有建立的部分。_disabled 不是内部标记而已 —— SchemaRenderer.tsx:461 把它交给被渲染组件:

disabled: __disabled || undefined,

(同一处 SchemaRenderer.tsx:411 把 _disabled 从 componentProps 里剥掉,正是为了改用这条显式通道。)所以任何接受 disabled prop 的渲染器 —— 输入控件、按钮、表单字段 —— 在元数据写了 disabled: '' 时会被真正置灰。

求值块在 evaluatedSchema 的 useMemo 里无条件执行(SchemaRenderer.tsx:285 起的可见性块之后,不带任何 type 判定),因此覆盖的是走 SchemaRenderer 的全部节点,而不是某个渲染器。这也正是 #3823 分诊时说「组件级 visible 门大多休眠,因为 SchemaRenderer 更早一层就判了」的那一层 —— 在 disabled 方向上,这一层自己就是缺陷所在。

严重度请分诊裁。证据取自源码链(file:line 如上),未另跑运行期探针 —— 探针属 #3849 之外的文件面,本单只做静态取证。

修法上的岔路(需先裁,不该由实施者顺手定)

想「统一改读那唯一定义」有个包依赖障碍:hasDeclaredVisibilityGate 定义在 packages/components/src/renderers/action/visibility-gate.ts,而 SchemaRenderer 在 packages/react。按仓内拓扑 components 依赖 react,反向导入是禁止的方向 —— 所以这里要么把定义上提(@object-ui/core 或 @object-ui/types 一侧),要么另想办法。#3842 已裁「不上提 core」,但那条裁定的语境是动作面五个落点,不是这条跨包路径;是否为了 SchemaRenderer 重新审视上提,是一个需要维护者裁的契约问题。

⛔ 明确不推荐:在 SchemaRenderer 里就地补一个 && !== '' —— 那就是把第四种「空」的拼法写进第四个消费者,#3842 / #3849 刚刚合掉的正是这类分身。

与 #3850 的关系

不是它的子集:#3850 收的是「空的范围」在 hasDeclaredVisibilityGate / toPredicateInput / evaluateCondition 三处不一致(空 source 信封那道夹缝),它的修法落点是 hasDeclaredVisibilityGate 那唯一定义或 producer 侧校验。SchemaRenderer 不调用 hasDeclaredVisibilityGate,自带一套内联判定,所以把 #3850 修完这一处仍然是错的 —— 按「附着而非散落」的判据,这属于独立单而非 #3850 的子单。但两单共享同一个「定义该放在哪一层」的裁定面,建议同批裁。

Related: #3842 / PR #3851(action:button + DeclaredActionsBar 两处,'' 半边已修)、#3849 / PR #3861(其余五处渲染落点,!= null → hasDeclaredVisibilityGate)、#3850(空 source 信封的范围不一致,同一裁定面)、#3848(执行入口 ActionRunner 的同族半边)、#3492(不变量出处)。未认领。

Activity

  1. os-zhuang commented on Aug 8, 2026

    @os-zhuang
    Contributor

    Triage: pm:queue (in-repo objectui card — no routing label by design).

    Classification basis — concrete defect with named anchors, all re-verified live on origin/main @ f0a625a (newer than the card's cited 993336f; nothing moved):

    • packages/react/src/SchemaRenderer.tsx disabled block: newSchema.disabled !== undefined → evaluator.evaluateCondition(newSchema.disabled) → _disabled = true — present as described.
    • packages/core/src/evaluator/ExpressionEvaluator.ts evaluateCondition: if (!condition) return true (comment says "undefined, null, empty string") — so disabled: "" and disabled: null both pass the !== undefined gate and evaluate truthy ⇒ _disabled = true.
    • Forwarding is real, not internal-only: the destructure strips _disabled and the createElement call re-injects disabled: __disabled || undefined — any renderer accepting a disabled prop greys out. The visible side is benign as the card says (negation flips the direction).
    • The block runs unconditionally in the evaluatedSchema useMemo for every node type — this is the generic path, hence third-spelling and wider than the != null family (null also caught).

    Dedup: repo-scoped search for SchemaRenderer + disabled returns only this card; zero open PRs touch this face. PR #3861 (#3849) covers the five renderer sites only; PR #3851 (#3842) covered the two action sites. No overlap — the family split is clean: #3848 (ActionRunner) / #3849 (five renderer sites) / #3850 (empty-source envelope in hasDeclaredVisibilityGate) / this card (SchemaRenderer inline gate).

    Direction note for the implementing lane: the card correctly rules out an in-place fourth spelling. The package-topology fork (uplift the single predicate-emptiness definition vs. another mechanism — components → react import is the forbidden direction) is the same ruling face as #3850's definition-layer question; recommend resolving both in one pass, serial after or batched with #3850. #3842's "no core uplift" ruling was scoped to the five action sites and does not automatically bind this cross-package path — if the implementer concludes an uplift is the only clean fix, that is a maintainer call to surface then, not now. No target: label — consistent with the rest of the family (requires unusual metadata shape; not a today's-user-path blocker).

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


    Generated by Claude Code

  2. os-project-manager commented on Aug 9, 2026

    @os-project-manager
    Collaborator

    Release board: target:v17 applied — class ①, SchemaRenderer's disabled !== undefined gate runs unconditionally for every node on the generic rendering path, so metadata carrying disabled: '' or disabled: null permanently greys out the control and the _disabled flag is forwarded as a real disabled prop. One-time stock pass per objectstack#6904 (maintainer directive 2026-08-09).


    Generated by Claude Code

  3. self-assigned this
    on Aug 9, 2026
  4. yinlianghui commented on Aug 9, 2026

    @yinlianghui
    ContributorAuthor

    🔒 认领(objectui 分片 PM,session_01GTRjn8xBqp75dk7kFupVRt,恢复批次 4)—— 与 #3850 结对,一 PR 双 Fixes,分支与文件面声明见 #3850 的认领评论(claude/issue-3850-3862-empty-predicate-gate)。本单的 SchemaRenderer !== undefined 第三拼法按裁决改读下沉后的唯一定义,⛔ 不就地加第四拼法。


    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

bugSomething isn't workingpm:dispatchedpm:queuetarget:v17v17 发布窗口工作集(GA 前排查 2026-08-04)

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions