我在我的 React 应用程序中使用 websockets。应用程序应该仅由函数组件组成。
想象一下,应用程序由两个"选项卡"组成:一个不相关,另一个是使用 websocket 的聊天。鉴于此,我们希望在用户进入聊天选项卡后建立 websocket 连接。
组件应如何处理 websocket 对象?由于我们想在用户切换回另一个选项卡(WebSocket.close()
(后进行清理,因此听起来我们应该在此处使用效果钩子。
const Chat = () => {
const [messages, setMessages] = useState([]);
useEffect(() => {
const webSocket = new WebSocket("ws://url");
webSocket.onmessage = (message) => {
setMessages(prev => [...prev, message.data]);
};
return () => webSocket.close();
}, []);
return <p>{messages.join(" ")}</p>;
};
工程!但是现在,假设我们想在useEffect
范围之外的某个地方引用webSocket
变量 - 比如说,我们希望在用户单击按钮后向服务器发送一些东西。
现在,应该如何实施?我目前的想法(尽管我觉得有缺陷(是:
const Chat = () => {
const [messages, setMessages] = useState([]);
const [webSocket] = useState(new WebSocket("ws://url"));
useEffect(() => {
webSocket.onmessage = (message) => {
setMessages(prev => [...prev, message.data]);
};
return () => webSocket.close();
}, []);
return <p>{messages.join(" ")}</p>;
};
有点有效,但我觉得有一个更好的解决方案。
正如@skyboyer在您的问题下的评论中所写的那样,您可以使用useRef
钩子来保存WebSocket
并且它会更正确,因为您不会更改或重新创建WebSocket对象。所以你不需要useState
钩子。
useRef(( Hook 不仅适用于 DOM refs。"ref"对象是一个通用容器,其当前属性是可变的,可以保存任何值,类似于类上的实例属性。
因此,您可以将代码更改为:
const Chat = () => {
const [messages, setMessages] = useState([]);
const webSocket = useRef(null);
useEffect(() => {
webSocket.current = new WebSocket("ws://url");
webSocket.current.onmessage = (message) => {
setMessages(prev => [...prev, message.data]);
};
return () => webSocket.current.close();
}, []);
return <p>{messages.join(" ")}</p>;
};
可能一个不错的选择是在单独的 WS 实例中管理 WS 操作,并在您需要的地方使用它,例如。
class WebSocketClient {
static instance = null;
callbacks = {};
static getInstance() {
if (!WebSocketClient.instance) WebSocketClient.instance = new WebSocketClient();
return WebSocketClient.instance;
}
constructor() {
this.socketRef = null;
}
addCallbacks = (...callbacks) => this.callbacks = { ...callbacks };
connect = () => {
const path = 'YOUR_SOCKET_PATH';
this.socketRef = new WebSocket(path);
this.socketRef.onopen = () => {
console.log('WebSocket open');
};
this.socketRef.onmessage = e => {
this.socketNewMessage(e.data);
};
this.socketRef.onerror = e => {
console.log(e.message);
};
this.socketRef.onclose = () => {
console.log("WebSocket closed let's reopen");
this.connect();
};
}
state = () => this.socketRef.readyState;
waitForSocketConnection = (callback) => {
const socket = this.socketRef;
const recursion = this.waitForSocketConnection;
setTimeout(
() => {
if (socket.readyState === 1) {
console.log("Connection is made")
if (callback != null) {
callback();
}
return;
} else {
console.log("wait for connection...")
recursion(callback);
}
},
1);
}
}
export default WebSocketClient.getInstance();
所以在你的组件中将是。
import React, { useEffect } from 'react';
import WSC from 'wsc';
const Test = ({}) => {
useEffect(()=>{
WSC.connect();
WSC.waitForSocketConnection(()=>{
'HERE_YOUR_CALLBACKS'
});
},[])
}
使用这种方式始终返回相同的实例。