5.3 设计模式迁移:Provider下沉与响应式解耦


5.3 设计模式迁移:Provider 下沉与响应式解耦

本节摘要:React 生态沉淀的模式清单——高阶组件、render props、容器与展示分离、受控组件、reducer 封装——各有既定的适用场景,而那些场景大多由"重渲染模型"制造。本节逐条盘点这些模式在 Solid 里的去向:保留的、变形的、直接退役的,并给出 children 函数与受控组件的对照代码。

「容器组件」在 React 语境里专指"把状态和请求收上来,通过 props 喂给纯展示组件"的那一层。它的存在理由是隔离:重渲染是常态,就把变化圈在容器层,让展示层尽量纯。Solid 里这条理由塌了——展示组件的绑定各自订阅,容器层不再承担"挡渲染"的职责,于是模式清单要重新审计。审计结果分三栏:保留、变形、退役。

逐条盘点:模式的去向

React 模式 解决的问题 Solid 去向 说明
高阶组件 HOC 复用逻辑、拦截渲染 退役 用组合原语替代;拦截渲染无从谈起
render props 把渲染权交给调用方 变形 children 函数,且天然响应式
容器与展示分离 圈住重渲染范围 弱化 分层仍有组织价值,挡渲染动机消失
Provider 包裹 跨层传值 保留 语义变为注入引用,见 5.1
受控组件 表单值单向可控 变形 信号直绑,双向但仍单向数据流
reducer 加 dispatch 集中化状态迁移 变形 store 加 produce 平移,样板骤减
条件渲染三元链 分支结构 变形 Show 与 Switch,见 4.3

表里"变形"栏占了一半,说明迁移不是推倒重来,而是同一意图换一套更短的实现。下面把两个最常用的变形展开。

render props 到 children 函数

React 里把渲染权下放的惯用写法是 render props——传一个函数当属性,组件在合适时机调用它。Solid 里直接用 children 函数,且由于组件只跑一次,这个函数内部读取的信号会自动成为响应式绑定:

// Solid:children 是函数,内部读取的资源变化会直达文本节点 function DataGate(props) { return ( <Suspense fallback={<Spin />}> {props.children} </Suspense> ); } // 使用方 <DataGate> <p>共 {count()} 条</p> </DataGate>

微妙之处在于:React 的 render props 函数随宿主渲染反复调用,Solid 的 children 函数求值一次、内部绑定终身有效。写出"每次都重新执行"的预期代码反而是错的。同理,Showwhen 分支支持回调形式拿到收窄值——render props 的常见用途在这里被内建成了控制流的一部分。

受控组件:信号直绑

React 的受控输入三板斧:value、onChange、setState。Solid 版本:

// React const [text, setText] = useState(""); <input value={text} onChange={e => setText(e.target.value)} /> // Solid const [text, setText] = createSignal(""); <input value={text()} onInput={e => setText(e.currentTarget.value)} />

形状依旧相近,机制走了一条更短的路:输入事件改信号,信号变化直写 value 属性绑定,全程没有"组件重渲染后再读新值"的回路。表单大时优势明显——React 每敲一个字符重跑一次表单组件,Solid 每敲一个字符只更新一个属性节点。双向绑定语法糖(Vue 的 v-model)Solid 没有提供,社区方案是一行组合原语包装,成本可控。

reducer 模式的平移

Redux 时代的"动作加 reducer"范式,核心诉求是状态迁移集中可测。迁移到 Solid:

// 原来:dispatch({type: "remove", id}) 进 reducer 返回新树 // 现在:store 加 produce,意图函数依然集中、依然可测 const remove = id => setState(produce(s => { const i = s.items.findIndex(it => it.id === id); if (i >= 0) s.items.splice(i, 1); }));

可测性没有损失——意图函数输入旧状态输出动作效果,测试照写。损失的是"时间旅行调试"这类依赖纯函数树的配套设施,Solid 生态里没有完全对位的工具;换来的是中间件链条、样板类型声明全部消失。团队要不要做这笔交换,取决于对调试工具的依赖深度,第 9 章的调试工具篇会给另一半信息。

💡 迁移代码库时别按"模式对模式"硬翻,按"问题对问题"重审:每个模式先问它在 React 里防的是什么,那个问题在 Solid 里是否还存在。存在则保留形态,不存在则用最短写法重表达。多数模式会瘦成一两行原语组合。

问题:依赖注入和全局单例,到底怎么选?

给一个二问判据。问一:同一个页面会不会出现多份实例?会(多标签组件、向导式分步表单、测试隔离),选 Context;不会(当前用户、购物车、主题),全局单例即可。问二:创建时机要不要晚于应用启动?要(依赖路由参数、依赖另一模块就绪),选 Context 让 Provider 控制时机。两问都答否,全局单例;任何一问答是,Context。这个判据比"看团队风格"可靠得多,写进 8.2 的评审清单完全够格。

给迁移代码库做一次模式清点

动手迁移前,建议先做半小时"模式清点":随机抽旧代码库的十个组件文件,数一数五种模式各出现几次——HOC 包装、render props、Provider 嵌套层数、容器组件、reducer 分发。清点结果直接预测迁移工作量:HOC 多的库(老牌表单与高阶连接器风格)改写面最大;render props 居多的中等;Provider 与容器为主的最轻——后两者在 Solid 里几乎按原语义保留。清点还能提前暴露"为绕开重渲染而生"的怪异代码(层层 memo、手动 shouldComponentUpdate 的类组件遗迹),这些代码迁移时不是翻译而是删除,别让它们污染新代码库的评审基线。

本节要点回顾

  • 模式清单要重审不要硬翻:防重渲染的动机大面积失效,保留的是组织价值。
  • HOC 退役:逻辑复用归组合原语,渲染拦截失去意义。
  • children 函数接棒 render props:求值一次、绑定终身,注意与 React 的执行预期不同。
  • 受控输入与 reducer 都有短形态:信号直绑与 store 加 produce,样板减半。

状态与模式的迁移盘完,下一章进入结构化部分:路由与全栈框架,对照 React Router 与 Next.js。


作者与出处
原作者: 灏天文库
来源:灏天文库
整理: 灏天文库整理
由灏天文库平台收录,内容或由平台用户上传,仅供学习交流
发布者: 作者: 灏天文库 转发
评论区 (0)
U