使用React钩子+ WebSockets的正确方法 [英] Proper way of using React hooks + WebSockets

查看:94
本文介绍了使用React钩子+ WebSockets的正确方法的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我需要连接到WebSockets服务器并记录其消息.使用React类组件,我会将这种逻辑放在 componentDidMount 生命周期挂钩中并愉快地继续前进,但是我不确定如何使用挂钩正确实现它.

I need to connect to WebSockets server and log it's messages. With React class component I'd put this logic in componentDidMount lifecycle hook and move on happily, but I'm not sure how to properly implement it with hooks.

这是我的第一次尝试.

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 状态来暂停日志记录.

I added connection and log logic to useEffect, provided empty array with dependencies, and everything worked great. Until I needed to add pause state to pause logging.

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服务器.显然这不是我想要的.

ESLint started to yell at me that I should add isPaused state as a dependency to useEffect.
Well, ok, done.
But I noticed re-connection to WS server after every time I click the button. This is clearly not what I want.

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

My next iteration was to use two useEffects: one for connection and one for message processing.

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挂钩.谢谢!

This works as expected, but I have a feeling that I miss something and this task can be solved easier, with one useEffect. Please help to refactor the code on convince me that I'm using React hooks in a proper way. Thanks!

推荐答案

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

As you are only setting the web socket once, I think a better approach is to use a ref instead of a state:

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");

        return () => {
            ws.current.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>
    );
}

useEffect 的顺序很重要.

这篇关于使用React钩子+ WebSockets的正确方法的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆