sockjs / sockjs-client

WebSocket emulation - Javascript client
MIT License
8.43k stars 1.3k forks source link

SockJS Client in React doesn't subscribe to the 'initial/queue' and one other topic #630

Closed marinavelvet closed 1 year ago

marinavelvet commented 1 year ago

Hi, I can't get SockJS Client to subscribe to the 'initial/queue' and 'dashboard/livestream' topic. I only get 'topic/livestream', no matter what I do. Here is my code snippet, and also a reminder that in the backend everything is configured fine, so the problem is somewhere either in this logic or in SockJs library. Please help!

import React from 'react'; import SockJsClient from 'react-stomp';

class TableDataStream extends React.Component { constructor(props) { super(props); this.state = { clientConnected: false, messages: [], data: [], circulatingSupply: '', figures: '', initialDataReceived: false }; }

componentDidMount() { this.setState({ clientConnected: true }); }

componentWillUnmount() { this.setState({ clientConnected: false }); }

sendMessage = () => { if (this.clientRef && this.clientRef.state && this.clientRef.state.connected) { console.log(this.clientRef); console.log(this.clientRef.state); console.log(this.clientRef.state.connected); this.clientRef.sendMessage('/dashboard/livestream', {}); } };

handleMessage = (event) => { if (event.topic === '/queue/initial') {

this.setState({ initialDataReceived: true }); } this.props.handleEvents(event);

};

handleDisconnect = () => { console.log('SockJSClient is disconnected'); };

render() { const { initialDataReceived, clientConnected } = this.state;

return (

{clientConnected && ( { this.handleMessage(msg); }} ref={(client) => { this.clientRef = client; }} onConnect={() => { this.sendMessage(); }} onDisconnect={this.handleDisconnect} debug={true} /> )}

);

} }

export default TableDataStream

jdratnayake commented 1 year ago

Issue: Unable to Subscribe to Multiple Topics with SockJS Client

Solution:

To resolve the problem of not being able to subscribe to 'initial/queue' and 'dashboard/livestream' topics with SockJS Client, make the following changes in the code:

  1. In the sendMessage method, update the topic to '/queue/initial' for initial data subscription:
    sendMessage = () => {
    if (this.clientRef && this.clientRef.state && this.clientRef.state.connected) {
    this.clientRef.sendMessage('/queue/initial', {}); // Change the topic to '/queue/initial'
    }
    };
  2. In the render method, ensure that you are subscribing to both topics:

    render() {
    const { initialDataReceived, clientConnected } = this.state;
    
    return (
    <div>
      {clientConnected && (
        <SockJsClient
          url="your_websocket_url_here" // Replace with your actual WebSocket URL
          topics={['/queue/initial', '/dashboard/livestream']} // Subscribe to both topics here
          onMessage={(msg) => this.handleMessage(msg)}
          ref={(client) => { this.clientRef = client; }}
          onConnect={() => { this.sendMessage(); }}
          onDisconnect={this.handleDisconnect}
          debug={true}
        />
      )}
    </div>
    );
    }

Remember to replace "your_websocket_url_here" with the correct WebSocket URL that connects to the backend server.

By implementing these changes, the SockJS Client should now successfully subscribe to both topics and receive messages accordingly.

If you encounter any further issues, feel free to ask for more assistance!

marinavelvet commented 1 year ago

Thank you for your answer. I've tried doing what you suggested and it still reads/shows only '/topic/livestream'. Can it maybe really be the backend problem or is there some issue with the library? I'm getting this message from console: id:sub-0

Also, this is the Vanilla JS code that manages to subscribe to those topics, but it seems that SockJS library doesn't have the methods proposed here, like 'subscribe'. I'm posting it as it may be of some help:

var stompClient = null;

window.onload = connect();

function setConnected(connected) { $("#connect").prop("disabled", connected); $("#disconnect").prop("disabled", !connected);

// Hides whole #conversation table. Commented so data stays on page after disconnect.

// if (connected) { // $("#conversation").show(); // } // else { // $("#conversation").hide(); // }

// Deletes transactions. Commented so data stays on page after disconnect.

// $("#transactions").html(""); }

function connect() { var socket = new SockJS('/register'); stompClient = Stomp.over(socket); stompClient.connect({}, function (frame) { setConnected(true); console.log('Connected: ' + frame); stompClient.subscribe('/topic/echo', function (echo) { showEcho(JSON.parse(echo.body).response); }); stompClient.subscribe('/topic/livestream', function (statistics) { showStatistics(JSON.parse(statistics.body)); }); stompClient.subscribe('/user/queue/initial', function (statistics) { showStatistics(JSON.parse(statistics.body)); });

   console.log('Before send to livestream');
   // Send message to livestream to get initial data
   stompClient.send("/dashboard/livestream", {}, "");
   console.log('Sent to livestream');
});

}

function disconnect() { if (stompClient !== null) { stompClient.disconnect(); } setConnected(false); console.log("Disconnected"); }

function sendName() { stompClient.send("/dashboard/echo", {}, JSON.stringify({'request': $("#name").val()})); console.log('Sent to echo'); }

function showEcho(message) { $("#transactions").prepend("<td colspan=\"4\">" + message + ""); }

function showStatistics(message) {

console.log("Message:" + message);
if (message.totalNGCs !== null) {
    document.getElementById("circulating_supply").innerHTML = message.totalNGCs;
}

if (message.dashboardFiguresData !== null) {
    document.getElementById("number_of_addresses").innerHTML = message.dashboardFiguresData.numberOfAddresses;
    document.getElementById("average_per_address").innerHTML = message.dashboardFiguresData.averageAmountPerAddress;
}

if (message.dashboardTransactionDataQueue !== null) {
    dtdq = message.dashboardTransactionDataQueue;
    dtdq.forEach(dtd => {
        $("#transactions").prepend("<tr><td>" + dtd.date + "</td>"
            + "<td>" + dtd.transactionId + "</td>"
            + "<td>" + "From: " + dtd.fromSerial + "<br>To: " + dtd.toSerial + "</td>"
            + "<td>" + dtd.amount + "</td></tr>");
    })
}

}

$(function () { $("form").on('submit', function (e) { e.preventDefault(); }); $( "#connect" ).click(function() { connect(); }); $( "#disconnect" ).click(function() { disconnect(); }); $( "#send" ).click(function() { sendName(); }); });

Noted that I can't use this code because I need to rewrite in for React framework. Any help is much appreciated.

marinavelvet commented 1 year ago

This issue is solved by adding componentDidUpdate method, so now the code looks like this:

class TableDataStream extends React.Component {
      constructor(props) {
      super(props);
      this.state = {
      clientConnected: false,
      messages: [],
      data: [],
      circulatingSupply: '',
      figures: ''
      }
      }

      sendMessage = () => {
      if (this.clientRef && this.clientRef.state && this.clientRef.state.connected) {
      this.clientRef.sendMessage("/dashboard/livestream", {});
      }
      }

      componentDidMount = () => {
      this.setState({ clientConnected: true});
      }

      componentDidUpdate = () => {
      if (this.state.clientConnected === true && this.clientRef && this.clientRef.state && this.clientRef.state.connected) {
      this.clientRef.sendMessage("/dashboard/livestream", {});
      this.setState({ clientConnected: false});
      }
      }

      handleMessage = (event) => {
      this.props.handleEvents(event);
      }

      handleDisconnect = () => {
      console.log('SockJSClient is disconnected');
      }

        render() {
        return (
            <div>
            <SockJsClient 
                url={window.ENVIRONMENT.REACT_APP_BACKEND_SERVICE_LOCATION + "/register"} 
                topics={["/user/queue/initial", "/topic/livestream"]}
                onMessage={(msg) => { this.handleMessage(msg); }}
                ref={(client) => {this.clientRef = client;}}
                onConnect={() => {
                  this.setState({ clientConnected: true});
                  this.sendMessage();}}
                onDisconnect={this.handleDisconnect}
              debug={true}
              />
            </div>
            );
            }
            }

    export default TableDataStream;
marinavelvet commented 1 year ago

This issue is solved by adding componentDidUpdate method, so now the code looks like this:

class TableDataStream extends React.Component {
      constructor(props) {
      super(props);
      this.state = {
      clientConnected: false,
      messages: [],
      data: [],
      circulatingSupply: '',
      figures: ''
      }
      }

      sendMessage = () => {
      if (this.clientRef && this.clientRef.state && this.clientRef.state.connected) {
      this.clientRef.sendMessage("/dashboard/livestream", {});
      }
      }

      componentDidMount = () => {
      this.setState({ clientConnected: true});
      }

      componentDidUpdate = () => {
      if (this.state.clientConnected === true && this.clientRef && this.clientRef.state && this.clientRef.state.connected) {
      this.clientRef.sendMessage("/dashboard/livestream", {});
      this.setState({ clientConnected: false});
      }
      }

      handleMessage = (event) => {
      this.props.handleEvents(event);
      }

      handleDisconnect = () => {
      console.log('SockJSClient is disconnected');
      }

        render() {
        return (
            <div>
            <SockJsClient 
                url={window.ENVIRONMENT.REACT_APP_BACKEND_SERVICE_LOCATION + "/register"} 
                topics={["/user/queue/initial", "/topic/livestream"]}
                onMessage={(msg) => { this.handleMessage(msg); }}
                ref={(client) => {this.clientRef = client;}}
                onConnect={() => {
                  this.setState({ clientConnected: true});
                  this.sendMessage();}}
                onDisconnect={this.handleDisconnect}
              debug={true}
              />
            </div>
            );
            }
            }

    export default TableDataStream;