缘由
db9feae(#197,v9.3.4)让类 view() 在构造函数里检查 isUsingStaticRendering(),为真则提前 return、不创建 reaction(避免 SSR 泄漏订阅)。但类组件构造只发生一次,而函数组件路径(observer/useObserver)是每次渲染重新检查同一标志。同一个 flag 在两类组件上的锁定高度不同,构造与渲染之间开启/关闭 flag 的实例行为不可预期。
现场复现(探针已验证,两个方向都坏)
// 方向 1:构造时开启,之后关闭 → 组件永久失去响应性
enableStaticRendering(true);
const V = view(class extends React.Component {
render() { return <div>{store.n}</div>; }
});
render(<V />); // 构造期 flag=true,无 reaction
enableStaticRendering(false); // 关闭(例如同进程里 SSR 完成后做 CSR/hydration 之后的交互)
store.n = 5;
// ❌ 探针实测:界面文本停在 '0',永不更新
// render() 走降级分支 super.render(),未追踪任何依赖
// 方向 2:构造时关闭,SSR 前才开启 → 仍然泄漏订阅
enableStaticRendering(false);
const V = view(class ... ); // 实例已在某处构造(模块级缓存、长生命周期容器)
renderToString(<V />); // 此时 enableStaticRendering(true)
// ❌ 该实例构造时已建 reaction,renderToString 不会 unmount,订阅照旧泄漏——
// 恰是 db9feae 要修的问题
问题分析
packages/react/src/view.tsx:56:构造函数一次性判断并决定是否创建 _reactiveRender,之后不再复核。
render() 里的降级分支(super.render())对方向 1 的情况没有任何"补建 reaction"的路径。
- 典型受害场景:SSR 与 CSR 混跑的进程(先 renderToString 再 hydrate/交互)、测试里全局开/关 flag。
修复建议(供修复 Agent 参考)
- 把检查时机从构造挪到 render(与函数组件对齐):
render() 里 if (isUsingStaticRendering()) return super.render(); 并在不满足时惰性创建 _reactiveRender(首次非静态渲染时建,注意只建一次、避免重复 observe)。
componentWillUnmount 已有 if (this._reactiveRender) 守卫,惰性化后依然安全。
- 需保留 db9feae 的原始目标:
enableStaticRendering(true) 下 renderToString 不建 reaction(惰性创建在 render 时判断即可满足——静态渲染期间 render 走 super 分支,永不创建)。
- 补回归测试:覆盖上面两个方向的探针场景。
验收标准
- 方向 1:关闭 flag 后 store 变化组件更新。
- 方向 2:静态渲染下不产生任何 reaction(可断言 reaction 计数或 unobserve 调用)。
packages/react/src/__tests__/view.test.tsx 全绿。
关联
影响版本
自 9.3.4 引入,当前 9.3.5 仍存在。
缘由
db9feae(#197,v9.3.4)让类
view()在构造函数里检查isUsingStaticRendering(),为真则提前 return、不创建 reaction(避免 SSR 泄漏订阅)。但类组件构造只发生一次,而函数组件路径(observer/useObserver)是每次渲染重新检查同一标志。同一个 flag 在两类组件上的锁定高度不同,构造与渲染之间开启/关闭 flag 的实例行为不可预期。现场复现(探针已验证,两个方向都坏)
问题分析
packages/react/src/view.tsx:56:构造函数一次性判断并决定是否创建_reactiveRender,之后不再复核。render()里的降级分支(super.render())对方向 1 的情况没有任何"补建 reaction"的路径。修复建议(供修复 Agent 参考)
render()里if (isUsingStaticRendering()) return super.render();并在不满足时惰性创建_reactiveRender(首次非静态渲染时建,注意只建一次、避免重复 observe)。componentWillUnmount已有if (this._reactiveRender)守卫,惰性化后依然安全。enableStaticRendering(true)下 renderToString 不建 reaction(惰性创建在 render 时判断即可满足——静态渲染期间 render 走 super 分支,永不创建)。验收标准
packages/react/src/__tests__/view.test.tsx全绿。关联
影响版本
自 9.3.4 引入,当前 9.3.5 仍存在。