React+TypeScript中使用非React对象注入组件树实现多态的更新问题
实现方案
方案1:使用MobX实现响应式类状态(推荐)
这是最贴合OOP开发习惯的方案,几乎不需要改动原有多态逻辑,只需要给状态类加上响应式标识即可实现自动更新:
- 安装依赖:
npm install mobx mobx-react - 改造状态类为响应式:
import { makeObservable, observable, action } from "mobx"; class ClickCounter { private count: number = 0; constructor() { makeObservable(this, { count: observable, IncrementCount: action }); } IncrementCount() { this.count += 1; } GetCount(): number { return this.count; } }
- 给App组件添加observer装饰器,自动响应类状态变更:
import { observer } from "mobx-react"; @observer export class App extends React.Component<{}, AppState> { // 原有代码完全不需要改动 }
该方案下,所有Operation实现类的内部状态变更都会被自动追踪,需要重渲染时MobX会自动触发组件更新,完全不需要手动传递刷新回调,也不破坏原有多态封装。
方案2:原生实现观察者模式(无第三方依赖)
如果不想引入额外库,可以自己实现轻量的变更通知机制:
- 扩展Operation接口,添加订阅能力:
interface Operation { HandleMouseDown(event: React.MouseEvent<HTMLDivElement>): void; GetComponents(): JSX.Element[]; // 新增订阅方法,返回取消订阅的清理函数 subscribe(changeCallback: () => void): () => void; }
- 改造Operation实现类,状态变更时通知订阅者:
class ClickCounterOperation implements Operation { private clickCounter: ClickCounter; private subscribers: Set<() => void> = new Set(); constructor() { this.clickCounter = new ClickCounter(); } subscribe(callback: () => void) { this.subscribers.add(callback); return () => this.subscribers.delete(callback); } HandleMouseDown(_: React.MouseEvent<HTMLDivElement>): void { this.clickCounter.IncrementCount(); // 状态变更后通知所有订阅者 this.subscribers.forEach(cb => cb()); } GetComponents(): JSX.Element[] { return [<div>you have clicked {this.clickCounter.GetCount()} times</div>]; } }
- 在App组件中订阅状态变更:
export class App extends React.Component<{}, AppState> { private unSubscribe?: () => void; constructor(props = {}) { super(props); const initialOperation: Operation = new ClickCounterOperation(); this.state = { currentOperation: initialOperation, }; // 订阅初始实例的状态变更,触发时强制组件重渲染 this.unSubscribe = initialOperation.subscribe(() => this.forceUpdate()); this.HandleMouseDown = this.HandleMouseDown.bind(this); } // 如果有运行时切换多态实例的需求,加上这个方法即可 switchOperation(newOperation: Operation) { // 清理旧实例的订阅 this.unSubscribe?.(); // 订阅新实例的变更 this.unSubscribe = newOperation.subscribe(() => this.forceUpdate()); this.setState({ currentOperation: newOperation }); } componentWillUnmount() { // 组件卸载时清理订阅,避免内存泄漏 this.unSubscribe?.(); } // 剩余原有逻辑保持不变 }
该方案没有任何第三方依赖,变更通知逻辑完全封装在Operation实现类内部,上层组件只需要做一次通用订阅,不需要给每个业务类传递定制化回调。
补充说明
两种方案都保留了你要的OOP多态特性,业务逻辑和状态完全封装在类内部,运行时切换多态实例的逻辑也不受影响,不需要把状态抽到外层组件或者全局store里,符合你的开发需求。
内容的提问来源于stack exchange,提问作者lsimmons
相关产品推荐
相关产品推荐

