带有功能组件的 WebSockets

IT技术 javascript reactjs react-hooks
2021-05-14 07:40:17

我在 React 应用程序中使用 websockets。应用程序应该仅由功能组件组成。

想象一下,该应用程序由两个“选项卡”组成:一个不相关,另一个是使用 websockets 的聊天。鉴于此,我们希望在用户进入聊天选项卡后建立一个 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>;
};

作品!但是现在,想象一下我们想要webSocketuseEffect范围之外的某个地方引用变量——比如说,我们想要在用户点击一个按钮后向服务器发送一些东西。

现在,应该如何实施?我目前的想法(虽然我觉得有缺陷)是:

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>;
};

有点工作,不过我觉得有一个更好的解决方案。

2个回答

正如@skyboyer 在您的问题下的评论中所写的那样,您可以使用useRef钩子来保持WebSocket它会更正确,因为您不会更改或重新创建 WebSocket 对象。所以你不需要useState钩子。

useRef() 钩子不仅仅用于 DOM 引用。“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'
        });
    },[])
}

用这种方式总是返回相同的实例。