Richtige Art der Verwendung von React-Hooks + WebSockets

Lesezeit: 4 Minuten

Ich muss eine Verbindung zum WebSockets-Server herstellen und seine Nachrichten protokollieren. Mit der React-Klassenkomponente würde ich diese Logik einfügen componentDidMount Lebenszyklus-Hook und glücklich weitermachen, aber ich bin mir nicht sicher, wie ich es mit Hooks richtig implementieren soll.

Hier mein erster Versuch.

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

Ich habe Verbindungs- und Protokolllogik hinzugefügt useEffect, stellte ein leeres Array mit Abhängigkeiten bereit, und alles funktionierte großartig. Bis ich hinzufügen musste pause Zustand, um die Protokollierung anzuhalten.

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 fing an, mich anzuschreien, dass ich hinzufügen sollte isPaused Zustand als Abhängigkeit von useEffect.
Na gut, fertig.
Aber ich habe eine erneute Verbindung zum WS-Server bemerkt, nachdem ich jedes Mal auf die Schaltfläche geklickt habe. Das ist eindeutig nicht das, was ich will.

Meine nächste Iteration war, zwei zu verwenden useEffects: eine für die Verbindung und eine für die Nachrichtenverarbeitung.

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

Dies funktioniert wie erwartet, aber ich habe das Gefühl, dass ich etwas verpasse und diese Aufgabe einfacher gelöst werden kann, mit einem useEffect. Bitte helfen Sie, den Code umzugestalten, und überzeugen Sie mich, dass ich React-Hooks auf die richtige Weise verwende. Vielen Dank!

Benutzeravatar von Alvaro
Alvaro

Da Sie den Web-Socket nur einmal festlegen, ist es meiner Meinung nach besser, eine Referenz anstelle eines Status zu verwenden:

Die Reihenfolge von useEffect ist wichtig.

Wie von George in den Kommentaren vorgeschlagen, im ersten useEffect ws.current wird in einer Variablen gespeichert, um sicherzustellen, dass wann close heißt es bezieht sich auf die gleiche Instanz.

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

  • Wenn ich diesen Code verwende, stelle ich fest, dass der Websocket nach jeder empfangenen Nachricht geschlossen und dann wieder geöffnet wird. Irgendwelche Ideen, warum es das tut?

    – Rykener

    27. September 2020 um 23:49 Uhr

  • Übrigens sollten Sie das neu erstellte Websocket in einer separaten Variablen speichern und es für die Bereinigung verwenden. Nicht der ws.current. Ein anderer Code kann mutieren ws.current und die alte Websocket-Instanz wird durch die Bereinigung nicht geschlossen. Die Verbindung bleibt offen und verursacht Fehler

    – George

    16. Juli 2021 um 11:53 Uhr

  • @Alvaro – in der Hoffnung, dass Sie sich bitte den Thread stackoverflow.com/questions/69476080/… ansehen könnten

    – ArthurJ

    7. Oktober 2021 um 5:55 Uhr

  • @Alvaro, ich habe auch Ihren genauen Code verwendet, um meine Websockets-Probleme zu lösen. Danke 🙂 Aber warum useRef? Ich habe die Dokumentation gelesen und es soll verwendet werden, um auf ein DOM-Element zu verweisen. Aber hier ist dies nicht der Fall. Könnte jemand erklären, wie es hier verwendet wird?

    – Gregor Woods

    17. November 2021 um 8:10 Uhr

  • Ich habe hier eine gute Antwort auf meine eigene Frage gefunden: dmitripavlutin.com/react-useref-guide Refs sind anscheinend nicht nur für DOM-Elemente. Die wichtigsten Punkte sind, dass sie zwischen Komponentenaktualisierungen bestehen bleiben und keine Komponentenaktualisierung auslösen, wenn der Wert geändert wird. Ideal für dieses Szenario

    – Gregor Woods

    17. November 2021 um 20:29 Uhr

 useEffect(() => {
const socket = new WebSocket('wss://ws.kraken.com');
socket.addEventListener('message', function (event) {
  const a = JSON.parse(event.data);
  setPriceWebSocket(a);

  const amin = socket.send(
    JSON.stringify({
      event: 'subscribe',
      pair: ['XBT/USD', 'XBT/EUR', 'ADA/USD'],
      subscription: { name: 'ticker' },
    }),
  );
  

});

  • So wie es derzeit geschrieben steht, ist Ihre Antwort unklar. Bitte bearbeiten Sie, um zusätzliche Details hinzuzufügen, die anderen helfen zu verstehen, wie dies die gestellte Frage beantwortet. Weitere Informationen zum Verfassen guter Antworten finden Sie in der Hilfe.

    – Gemeinschaft
    bot

    6. Juni um 14:47 Uhr

1434360cookie-checkRichtige Art der Verwendung von React-Hooks + WebSockets

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy