✓ 核心根治 双层防御架构 LinkLegal / NewOriginDetail Commit: 3cfb5e99 & d0147cb8

Origin 存证导入事件污染与白屏修复方案

针对 AEM 11566 告警:TypeError: S.filter is not a function 彻底阻断污染源与消费端容错设计
双层防御架构原理 (Two-Layer Defensive Architecture)
遵循 Linus 好品味原则:消灭数据结构层面的特殊情况。不仅在写入端切断 React 合成事件对象的污染,更在消费端建立不可击穿的类型归一化。
修复前:单点脆弱,事件穿透 Crash Path
1
Fusion Dialog onOk 触发
<Dialog onOk={handleOk} />
点击时底层抛出 SyntheticEvent e
2
形参默认值失效
handleOk = (nextResult = result) => {}
入参为 Event(真值),默认值不生效!
nextResult = SyntheticEvent
3
非法对象写入 React 状态
setSource(Event); triggerChange(Event);
整个存证列表数据结构蜕变为 Object
4
renderItem 消费端崩溃
source?.filter(...)
Object 没有 filter 方法,报错:
TypeError: S.filter is not a function 导致整树白屏
修复后:写入截断 + 消费兜底 100% Robust
1
写入端:箭头函数封装,阻断入参
<Dialog onOk={() => handleOk(result)} />
彻底屏蔽底层的 SyntheticEvent,只传递显式结果
2
控制流:Array.isArray 强校验归一
let list = EMPTY_ARRAY;
if (Array.isArray(nextResult)) list = nextResult;
else if (Array.isArray(result)) list = result;
list 绝对保证为 Array 类型
3
状态写入:清洁数组入库
setSource(list); triggerChange(list);
消灭非数组污染源头
4
消费端:安全守卫兜底
const safeSource = Array.isArray(source) ? source : EMPTY_ARRAY;
safeSource.filter(...)
零运行时崩溃风险
Surgical Diff: src/containers/NewOriginDetail/components/LinkLegal/index.js
113 onChange(changedValue);
114 };
115
116 -- const handleOk = (nextResult = result) => {
116 ++ const handleOk = (nextResult) => {
117 ++ let list = EMPTY_ARRAY;
118 ++ if (Array.isArray(nextResult)) {
119 ++ list = nextResult;
120 ++ } else if (Array.isArray(result)) {
121 ++ list = result;
122 ++ }
123 setVisible(false);
124 -- setSource(nextResult);
125 -- triggerChange(nextResult);
124 ++ setSource(list);
125 ++ triggerChange(list);
126 };
... @@ -142,7 +148,8 @@ renderItem 消费端防御 @@
147 recordTypes.forEach((r, index) => {
148 -- const selectedRecords = source?.filter(t => t.type === r) || [];
148 ++ const safeSource = Array.isArray(source) ? source : EMPTY_ARRAY;
149 ++ const selectedRecords = safeSource.filter(t => t.type === r);
150 if (recordType && selectedRecords.length === 0) return;
... @@ -190,7 +197,7 @@ Dialog onOk 调用点截断 @@
196 className="link-dialog common-Dialog-style"
197 visible={visible}
198 -- onOk={handleOk}
198 ++ onOk={() => handleOk(result)}
199 onClose={handleCancel}
200 okText="导入"
运行时类型推导与崩溃仿真 (Live Behavioral Test)
模拟不同触发场景下,旧版本(1.0.113)与新版本(1.0.114 / 2.2.8)的执行轨迹。
旧代码逻辑运行表现 (Old Logic)
修复后逻辑运行表现 (Fixed Logic)
深度解答:为什么不能只改 `onOk={() => handleOk(result)}` 这一个?
核心直陈:只改 onOk={() => handleOk(result)} 属于“头痛医头”的点状补丁(Symptom Fix)。它把系统的健壮性完全押注在「唯一一个 JSX 属性绝对不出错」的假设上。一旦有第二处调用、父组件异常传参或未来组件重构,脆弱的防线会瞬间瓦解,白屏必定复发。
软肋 1:脆弱的隐式函数契约
旧函数签名是 (nextResult = result) => ...
JavaScript 默认形参机制只有在入参严格 === undefined 时才生效
若只在 Dialog 改一行,handleOk 本身依然毫无防备。任何地方一旦传递 null0false 或其他非数组对象,默认值立即失效,脏对象直接写入 state!
软肋 2:handleOk 存在多处间接调用
注意源码 L159:
afterClose={() => handleCheckbox(false, item, handleOk)}
handleOk 被作为高阶函数回调 cb 传入了 handleCheckbox
把安全性交给调用方是危险的。函数必须在自身的入口处对入参数据结构建立强校验。
软肋 3:污染扩散至父表单与后端
handleOk 内部紧接着执行了 triggerChange(nextResult),直接驱动外部 Form 的 onChange
若不做类型收窄,非数组脏数据会向外逃逸,不仅导致当前组件崩毁,还会污染整个提交 Payload,导致后端持久化脏数据。
单行修复 vs 纵深防御 抵抗场景矩阵 (Resilience Matrix)
可能发生的真实场景 方案 A:只改 onOk 这一处 方案 B:当前提交的完整修复 (3处)
场景 1:用户点击弹窗「导入」按钮 ✓ 解决(不再把事件传给 handleOk) ✓ 彻底解决
场景 2:未来他人重构或组件升级
(例如 Dialog 升级、或抽取了通用弹窗函数)
✗ 极易穿透
如果新人写了 onOk={handleOk},白屏立即原样复发。
✓ 免疫
handleOk 入口 Array.isArray 自动拦截并清洗降级。
场景 3:调用方传入异常非数组数据
(如 afterClose 回调、接口异常返回 null 等)
✗ 崩溃
直接写进 source,导致 renderItem 崩溃白屏。
✓ 免疫
handleOk 强行归一化为安全数组。
场景 4:消费端 renderItem 遍历
(不管 state 因任何原因异常蜕变)
✗ 脆弱
source?.filter 遇到非数组 Object 必抛 TypeError,React 整树卸载白屏。
✓ 免疫
safeSource = Array.isArray ? source : [],保证 100% 数组安全,绝不整页白屏。
Linus 代码品味(Good Taste)与第一性原理:
1. 消灭特殊情况:好的代码不会假设“外部所有的调用者都是正人君子”。把非法输入在入口处收窄为合法情形,整个系统的后续代码就无需处理异常分支。
2. 爆炸半径控制(Blast Radius):在前端弱类型环境下,消费端渲染是绝对不可崩溃的底线。渲染崩溃会导致整页 React 树卸载(白屏),而消费端 safeSource 守卫将不可逆的整页崩溃降级为了局部的安全降级。
双分支发布状态与当前卡点 (Deployment Pipeline Tracking)
分支 1: master (主干标准版) ➔ 迭代 1.0.114 (ID: 17026437)
✓ 集成构建 (12691459)
✓ 日常环境 (Daily Deploy)
⏳ 线上发布 (Production Pending)
分支 2: trunk-business (业务定制版) ➔ 迭代 2.2.8 (ID: 17026432)
✓ 集成构建 (12691463)
✓ 日常环境 (Daily Deploy)
⏳ 线上发布 (Production Pending)
为什么线上今早(09-14 09:44)还在报告警?
线上 CDN 当前仍部署着 1.0.113 的代码资源(OriginDetail.bc7e2a8d.chunk.js)。
只要在 O2 控制台为这两个迭代点击「发起线上发布」,CDN 资源即完成平滑切换到 1.0.1142.2.8,线上报错即刻清零。