reactjs 具有功能组件的WebSocket

2j4z5cfb  于 2023-03-01  发布在  React
关注(0)|答案(2)|浏览(159)

我在我的React应用程序中使用了websockets,这个应用程序应该只包含功能组件。
假设应用程序由两个“选项卡”组成:一个是无关的,另一个是使用websocket的聊天。2考虑到这一点,我们希望在用户进入聊天选项卡后立即建立websocket连接。
组件应该如何处理WebSocket对象?因为我们希望在用户切换回另一个标签页(WebSocket.close())时进行清理,所以听起来我们应该在这里使用一个effect钩子。

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

挺管用的,不过我觉得有更好的解决办法。

92dk7w1h

92dk7w1h1#

正如@skyboyer在你的问题下面的评论中所写的,你可以使用useRef钩子来保持WebSocket,这会更正确,因为你不打算改变或重新创建WebSocket对象。
useRef()Hook不只是用于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>;
};
dzhpxtsq

dzhpxtsq2#

也许一个好的选择是在一个单独的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'
        });
    },[])
}

用这种方法总是返回相同的示例。

相关问题