Skip to content

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

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 完全相同 —— 同一个 truevisible 上意味着「显示」(该侧 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_disabledcomponentProps 里剥掉,正是为了改用这条显式通道。)所以任何接受 disabled prop 的渲染器 —— 输入控件、按钮、表单字段 —— 在元数据写了 disabled: '' 时会被真正置灰。

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

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

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

想「统一改读那唯一定义」有个包依赖障碍:hasDeclaredVisibilityGate 定义在 packages/components/src/renderers/action/visibility-gate.ts,而 SchemaRendererpackages/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(其余五处渲染落点,!= nullhasDeclaredVisibilityGate)、#3850(空 source 信封的范围不一致,同一裁定面)、#3848(执行入口 ActionRunner 的同族半边)、#3492(不变量出处)。未认领。

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingpm: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