React Native 对接 Django Channels 如何正确创建管理 WebSocket 避免问题
React Native + Django Channels WebSocket连接问题解决方案
问题根因
TypeError: chatSocket.close is not a function报错是因为闭包陷阱:useEffect中捕获的chatSocket是初始值(空对象),setSocket更新state是异步的,触发beforeRemove事件时拿到的仍然是初始的空对象,没有close方法。- 旧连接未关闭是因为useEffect没有配置正确的依赖项和清理函数,切换房间/退出页面时不会主动销毁旧连接实例。
- 重渲染重复创建连接是因为直接在组件顶层初始化WebSocket,每一次组件重渲染都会执行构造函数新建连接。
正确实现方案
推荐用useRef存储WebSocket实例,比useState更适合:useRef的current属性是可变的,不会触发组件重渲染,也不存在state更新的异步和闭包问题。
前端代码示例
import {useRef, useState, useEffect} from 'react'; const ChatScreen = ({navigation, route}) => { const room = route.params.item; // 用ref存储socket实例 const chatSocketRef = useRef(null); const [chat, setChat] = useState([]); const [message, setMessage] = useState(''); const getMsgs = () => { apiServer.get(`/chat/api/rooms/${room.id}`) .then((resp) => setChat(resp.data)) .catch((e) => console.log(e)); }; const sendMsg = () => { // 发送前先判断连接状态,避免报错 if (chatSocketRef.current?.readyState === WebSocket.OPEN) { chatSocketRef.current.send(JSON.stringify({ message, room: room.id, owner: 1 })); } }; useEffect(() => { // 初始化新连接 const socket = new WebSocket(`ws://192.168.1.14:8000/ws/chat/${room.name}/`); chatSocketRef.current = socket; // 绑定事件监听 socket.onmessage = () => getMsgs(); socket.onerror = (e) => console.log('WebSocket错误:', e); socket.onclose = (e) => console.log('连接关闭:', e.code, e.reason); // 路由离开前关闭连接 const beforeRemoveListener = navigation.addListener('beforeRemove', () => { if (chatSocketRef.current?.readyState === WebSocket.OPEN) { chatSocketRef.current.close(1000, '用户主动离开房间'); } chatSocketRef.current = null; }); // 清理函数:组件卸载、切换房间时自动销毁旧连接 return () => { beforeRemoveListener(); if (chatSocketRef.current?.readyState === WebSocket.OPEN) { chatSocketRef.current.close(1000, '组件卸载/切换房间'); } chatSocketRef.current = null; }; // 依赖加入room.name,切换房间时自动重建连接 }, [navigation, room.name]); return ( <TouchableOpacity onPress={() => { sendMsg(); setMessage(''); }} > 发送消息 </TouchableOpacity> ) }
后端注意事项
你现有ChatConsumer的disconnect逻辑是正确的,会自动将连接从房间组中移除,不会出现关闭连接后仍然收到消息的问题,只需要按需添加日志排查连接关闭情况即可。
可选优化
如果你的应用需要后台保活WebSocket,不需要每次退出房间就关闭连接,可以将socket实例提到全局状态(比如Context、Redux)中管理,只在用户登出、网络切换时重建/关闭连接,减少连接重建的开销。
内容的提问来源于stack exchange,提问作者Hazem gamal
相关产品推荐
相关产品推荐

