Skip to content

SchemaRenderer 的 hidden / hiddenOn 是 #3862 的反极性孪生:hidden: '' / null / 空 source 信封在通用渲染路径上让节点永久消失 #3955

Description

@yinlianghui

Blocked-by: #3850

来源

发现自 #3850 + #3862 的结对实施(PR 分支 claude/issue-3850-3862-empty-predicate-gate)。给 SchemaRendererdisabled 链写「visible 家族等价钉」时,顺手把同一个 useMemo可见性链的另一极性 也量了一遍,发现 hidden / hiddenOn 两条腿有与 #3862 完全同构的缺陷,而且后果比置灰更重:节点整个消失。

不在 #3850 / #3862 的修法面:那两单裁决的落点是「已声明门」定义的下沉 + disabled / disabledOn 两条腿改读,visible 家族一侧被明确裁为保持 !== undefined 不动(它的 true 被取反,与「没有门」同结果,且改动会改变别名优先级)。hidden 腿的 true 被取反,所以同一个「空谓词 → evaluateConditiontrue」在这里意味着 HIDE。故独立成单,未认领

机理

packages/react/src/SchemaRenderer.tsx(origin/main @ 65bb513dc073deb4e72950c287eef32cb56bf8ce),shouldHide 块(287 行起)的最后两条腿:

if (newSchema.hidden !== undefined) {
  return evaluator.evaluateCondition(newSchema.hidden);   // 308-310:不取反
}
if (newSchema.hiddenOn !== undefined) {
  return evaluator.evaluateCondition(newSchema.hiddenOn); // 311-313:不取反
}

ExpressionEvaluator.evaluateCondition 对「没有条件」的唯一默认是 return true(意为 visible/enabled)。前四条腿(visible / visibleWhen / visibleOn / visibility)都写成 !evaluateCondition(...),那个 true 取反后落在「不隐藏」,与「没有门」同结果 —— 良性。hidden / hiddenOn 直接返回,于是 true = 隐藏:空谓词让节点消失,与 #3862disabled 上的 true = 置灰是同一个不对称的第二个出口。

拼法同样是最宽的那一格 !== undefined,所以 hidden: null 也命中(null !== undefined 为真)。

实测(worktree @ 65bb513dc,一次性探针,未提交)

同一个 probe 组件,只换 key,rendered / HIDDEN 是节点是否出现在 DOM:

value                        | visible  | visibleWhen | visibleOn | visibility | hidden   | hiddenOn
''                           | rendered | rendered    | rendered  | rendered   | HIDDEN   | HIDDEN
'   '(纯空白)               | rendered | rendered    | rendered  | rendered   | HIDDEN   | HIDDEN
{dialect:'cel',source:''}    | rendered | rendered    | rendered  | rendered   | HIDDEN   | HIDDEN
null                         | rendered | rendered    | rendered  | rendered   | HIDDEN   | HIDDEN
false                        | HIDDEN   | HIDDEN      | HIDDEN    | HIDDEN     | rendered | rendered
true                         | rendered | rendered    | rendered  | rendered   | HIDDEN   | HIDDEN

false / true 两行都正确(声明了 verdict 就按 verdict 走),坏的正是四行「空」。

为什么值得管

修法(机械,已有唯一定义可读)

#3850 的裁决已经把「已声明门」下沉为 @object-ui/corehasDeclaredPredicate(packages/core/src/evaluator/declaredPredicate.ts,该 PR 落地),disabled / disabledOn 两条腿已改读它。这里同样改读即可:

if (hasDeclaredPredicate(newSchema.hidden)) { ... }
if (hasDeclaredPredicate(newSchema.hiddenOn)) { ... }

⛔ 不要就地补 && !== '' —— 那是把第 N 种「空」拼法写进第 N 个消费者,#3842 / #3849 / #3850 三单合掉的正是这类分身。

需要注意的一处非等价:hidden 腿改读后,hidden: ''继续往下落到 hiddenOn(而不是在空谓词上短路),这与 disabled 链在 #3862 修后的行为一致,但它改变了别名优先级,应作为行为变更钉住而不是当作等价。另外前四条 visible 腿是否也统一改读,是一个独立的判断:那侧空谓词良性,改读会改变别名优先级(如 visible: '' + hidden: 'true' 的组合),收益是「一个概念一处定义」,代价是行为变更 —— 建议与本单一起裁,而不是在本单里顺手改。

Related: #3862(同一文件、同一 useMemodisabled 极性的同族半边)、#3850(「已声明门」范围裁决与定义下沉)、#3842 / #3849 / #3848(动作面与执行入口的同族)、#3492(不变量出处)。未认领。


Generated by Claude Code

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions