@preact/signals-react v2 是否支持React类组件?
问题
Signals的React集成文档仅提及了与函数组件的集成方式,我尝试在类组件中渲染signal值,但完全不具备响应性。
测试代码如下:
"dependencies": { "react": "^18.2.0", "react-dom": "^18.2.0", "@preact/signals-react": "^2.0.0", "@preact/signals-react-transform": "^0.3.0", }
import React from "react"; import { createRoot } from "react-dom/client"; import { signal } from "@preact/signals-react"; const count = signal(0); // 不会重新渲染 class CountClass extends React.Component { render() { return ( <h1 onClick={() => { count.value = Math.random(); }} > title{count.value} </h1> ); } } // 可以正常重新渲染 const CountFunc = () => { return ( <h1 onClick={() => { count.value = Math.random(); }} > title{count.value} </h1> ); }; const App = () => { return ( <div> <CountClass /> <CountFunc /> </div> ) } createRoot(document.querySelector("#root")).render(<App />);
用Babel转换后,类组件部分未生成任何与signal相关的转换代码:
//... const count = (0, _signalsReact.signal)(0); // 不会重新渲染 class CountClass extends _react.default.Component { render() { return /*#__PURE__*/_react.default.createElement("h1", { onClick: () => { count.value = Math.random(); } }, "title", count.value); } } // 可以正常重新渲染 const CountFunc = () => { var _effect = (0, _runtime.useSignals)(1); try { return /*#__PURE__*/_react.default.createElement("h1", { onClick: () => { count.value = Math.random(); } }, "title", count.value); } finally { _effect.f(); } }; //...
解决方案
核心原因
Signals的React集成(包括Babel转换插件)默认仅对函数组件生效,会自动注入useSignals钩子追踪signal依赖并触发重渲染。类组件没有这种自动处理机制,直接访问count.value只会获取当前快照值,不会订阅更新,因此signal变化时类组件不会重新渲染。
可行解决方法
方法1:手动订阅signal更新触发重渲染
在类组件生命周期中手动订阅signal变化,通过setState触发组件重渲染:
class CountClass extends React.Component { componentDidMount() { // 订阅signal变化,更新时调用setState触发重渲染 this.unsubscribe = count.subscribe(() => this.setState({})); } componentWillUnmount() { // 卸载时取消订阅,避免内存泄漏 this.unsubscribe(); } render() { return ( <h1 onClick={() => { count.value = Math.random(); }} > title{count.value} </h1> ); } }
方法2:用函数组件做中间层
将类组件包裹在函数组件内部,借助函数组件的自动追踪能力,把signal值通过props传递给类组件:
const CountClassWrapper = () => { // 函数组件内访问signal会自动触发依赖追踪 const currentCount = count.value; return <CountClass count={currentCount} />; }; class CountClass extends React.Component { render() { return ( <h1 onClick={() => { count.value = Math.random(); }} > title{this.props.count} </h1> ); } }
方法3:封装高阶组件(HOC)复用订阅逻辑
编写通用HOC,给类组件添加signal订阅能力:
function withSignals(WrappedComponent) { return class extends React.Component { state = {}; unsubscribes = []; componentDidMount() { // 批量订阅传入的signal列表 this.props.signals.forEach(signal => { this.unsubscribes.push(signal.subscribe(() => this.setState({}))); }); } componentWillUnmount() { // 清理所有订阅 this.unsubscribes.forEach(unsubscribe => unsubscribe()); } render() { return <WrappedComponent {...this.props} />; } }; } // 使用HOC包裹类组件 const CountClassWithSignals = withSignals(CountClass); // 使用时传入需要订阅的signal <CountClassWithSignals signals={[count]} />
方法4:迁移到函数组件(官方推荐)
如果项目允许,直接将类组件转换为函数组件,即可直接使用Signals的自动响应式能力,这也是官方文档推荐的集成方式。
内容的提问来源于stack exchange,提问作者Littlee
相关产品推荐
相关产品推荐

