Skip to content

react: 类组件 view() 在构造期锁死 static rendering 标志,与函数组件检查时机不一致 #254

Description

@ximing

缘由

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 仍存在。

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions