javascript - React hooks + WebSockets 的正确使用方法

标签 javascript reactjs websocket react-hooks

我需要连接到 WebSockets 服务器并记录它的消息。使用 React 类组件,我会将这个逻辑放入 componentDidMount 生命周期钩子(Hook)中并愉快地继续,但我不确定如何使用钩子(Hook)正确实现它。

这是我的第一次尝试。

import React, {useEffect} from 'react';

export default function AppWs() {
  useEffect(() => {
    let ws = new WebSocket('wss://ws.kraken.com/');
    ws.onopen = () => console.log('ws opened');
    ws.onclose = () => console.log('ws closed');

    ws.onmessage = e => {
      const message = JSON.parse(e.data);
      console.log('e', message);
    };

    return () => {
      ws.close();
    }
  }, []);

  return (
    <div>hooks + ws</div>
  )
}

我向 useEffect 添加了连接和日志逻辑,提供了具有依赖项的空数组,一切都运行良好。直到我需要添加 pause 状态来暂停日志记录。

export default function AppWs() {
  const [isPaused, setPause] = useState(false);

  useEffect(() => {
    let ws = new WebSocket('wss://ws.kraken.com/');
    ws.onopen = () => console.log('ws opened');
    ws.onclose = () => console.log('ws closed');

    ws.onmessage = e => {
      if (isPaused) return;
      const message = JSON.parse(e.data);
      console.log('e', message);
    };

    return () => {
      ws.close();
    }
  }, []);

  return (
    <div>
      <button onClick={() => setPause(!isPaused)}>{isPaused ? 'Resume' : 'Pause'}</button>
    </div>
  )
}

ESLint 开始对我大喊大叫,说我应该添加 isPaused 状态作为 useEffect 的依赖项。
嗯,好的,完成。
但我注意到每次单击按钮后都会重新连接到 WS 服务器。这显然不是我想要的。

我的下一次迭代是使用两个 useEffect:一个用于连接,一个用于消息处理。

export default function AppWs() {
  const [isPaused, setPause] = useState(false);
  const [ws, setWs] = useState(null);

  useEffect(() => {
    const wsClient = new WebSocket('wss://ws.kraken.com/');
    wsClient.onopen = () => {
      console.log('ws opened');
      setWs(wsClient);
    };
    wsClient.onclose = () => console.log('ws closed');

    return () => {
      wsClient.close();
    }
  }, []);

  useEffect(() => {
    if (!ws) return;

    ws.onmessage = e => {
      if (isPaused) return;
      const message = JSON.parse(e.data);
      console.log('e', message);
    };
  }, [isPaused, ws]);

  return (
    <div>
      <button onClick={() => setPause(!isPaused)}>{isPaused ? 'Resume' : 'Pause'}</button>
    </div>
  )
}

这按预期工作,但我有一种感觉,我错过了一些东西,并且通过一个 useEffect 可以更轻松地解决此任务。 请帮助重构代码,让我相信我正在以正确的方式使用 React hooks。谢谢!

最佳答案

由于您只设置一次网络套接字,我认为更好的方法是使用引用而不是状态:

useEffect 的顺序很重要。

正如 George 在评论中所建议的,在第一个 useEffect 中,ws.current 被保存到一个变量中,以确保当 close被称为它引用同一个实例。

export default function AppWs() {
    const [isPaused, setPause] = useState(false);
    const ws = useRef(null);

    useEffect(() => {
        ws.current = new WebSocket("wss://ws.kraken.com/");
        ws.current.onopen = () => console.log("ws opened");
        ws.current.onclose = () => console.log("ws closed");

        const wsCurrent = ws.current;

        return () => {
            wsCurrent.close();
        };
    }, []);

    useEffect(() => {
        if (!ws.current) return;

        ws.current.onmessage = e => {
            if (isPaused) return;
            const message = JSON.parse(e.data);
            console.log("e", message);
        };
    }, [isPaused]);

    return (
        <div>
            <button onClick={() => setPause(!isPaused)}>
                {isPaused ? "Resume" : "Pause"}
            </button>
        </div>
    );
}

关于javascript - React hooks + WebSockets 的正确使用方法,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/60152922/

相关文章:

javascript - 正则表达式解析带有转义字符的字符串

javascript - 根据字符串数组重新排序对象数组?

javascript - Ajax GET 请求不显示 URL 中的参数

javascript - JEST 和 ES6 导入 - 基于根文件夹的导入不起作用

javascript - 使用 Websockets 的同步请求

javascript - 如何自引用 NodeJS 模块?

javascript - React js 组成

javascript - 使用 Highcharts-React 拖放不起作用

android - 如何在 Android 上将 socket.io 作为连续服务运行而无需 sleep 或暂停?

python - Flask-socketio 服务器和 python-socketio 之间的安全通信