class组件中websocket必须在componentdidmount创建、componentwillunmount关闭,显式绑定事件并手动清理,否则易致内存泄漏、重复连接或状态错乱。

Class组件中用 WebSocket 本身没问题,但必须手动管理生命周期,否则极易内存泄漏、重复连接、状态错乱。
componentDidMount 中创建连接并绑定事件
这是唯一安全的初始化位置。不能在 constructor 或 render 里 new WebSocket,否则每次渲染都新建实例;也不能在 componentWillMount(已废弃)里操作。
- 在
componentDidMount中创建WebSocket实例,并立即赋值给this.ws - 必须显式绑定所有事件处理器:
onopen、onmessage、onclose、onerror,且函数需是类方法或箭头函数(避免 this 丢失) - 不要在事件回调里直接调用
this.setState前检查this.mounted—— Class 组件没有内置挂载标记,得自己维护
componentDidMount() {
this.ws = new WebSocket('wss://echo.websocket.org');
this.ws.onopen = () => {
console.log('✅ 已连接');
this.setState({ status: 'connected' });
};
this.ws.onmessage = (event) => {
this.setState(prev => ({
messages: [...prev.messages, event.data]
}));
};
this.ws.onclose = () => {
console.log('❌ 连接关闭');
this.setState({ status: 'disconnected' });
};
this.ws.onerror = (err) => {
console.error('⚠️', err);
};
}
componentWillUnmount 中必须关闭连接并清理引用
这是最容易被忽略、也最危险的一环。不清理会导致:WebSocket 实例持续持有对组件的引用 → 组件无法被 GC → 内存泄漏;断连后 onmessage 仍可能触发 → this.setState 在已卸载组件上调用 → React 警告甚至崩溃。
- 务必在
componentWillUnmount中调用this.ws.close() - 关闭后立即将
this.ws设为null,防止后续误用 - 如果用了
setInterval或自定义心跳定时器,也要在这里clearInterval
componentWillUnmount() {
if (this.ws && this.ws.readyState === WebSocket.OPEN) {
this.ws.close();
}
this.ws = null;
if (this.heartbeatTimer) {
clearInterval(this.heartbeatTimer);
}
}
为什么不能依赖 shouldComponentUpdate 或 componentDidUpdate 做重连?
重连逻辑不属于渲染流程,强行塞进更新周期会破坏响应性,且极易陷入无限 setState 循环。
-
shouldComponentUpdate是纯函数,不应有副作用(如发起新连接) -
componentDidUpdate在每次状态变更后触发,若在其中检测status === 'disconnected'就重连,可能因网络抖动反复触发,造成雪崩式重试 - 正确做法是把重连逻辑封装成独立方法(如
reconnect()),并在onclose/onerror中按策略调用(例如带指数退避)
Class 组件里处理心跳和自动重连的实际约束
React Class 组件没有 Hooks 提供的 useRef 或闭包稳定性保障,所有重连控制变量(如重试次数、退避延迟)都得挂在 this 上,且必须小心处理异步回调中的 this 指向和竞态。
- 用
this.reconnectCount = 0记录当前重试次数,每次重连失败后递增 - 用
setTimeout实现退避,延迟 =Math.min(30000, 1000 * Math.pow(2, this.reconnectCount)) - 在
onclose中判断event.code !== 1000(正常关闭)才触发重连,避免用户主动退出时误恢复 - 重连前先检查
this.ws === null || this.ws.readyState === WebSocket.CLOSED,防止重复 new
Class 组件的生命周期管理本身不复杂,但一旦加入重连、心跳、消息队列等真实需求,状态分散、this 不稳定、清理易遗漏等问题就会集中爆发。这不是写法问题,而是 Class 模式在副作用管理上的结构性局限 —— 它把“何时做”(生命周期)和“做什么”(业务逻辑)强行耦合,而现代实时通信恰恰需要解耦与精细控制。











