Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

React and Socket.io: Able to get initial data - but view doesn't update when a new post comes in

Not sure if the issue is how I have my sockets setup - or if I am incorrectly trying to render the data with React.

I can successfully pull in data with my socket - yet it doesn't live update state when new data is posted to the server. My intention is for the state in React to automatically render new data, which is always live because of the socket connection.

Here is my client app that gets messages from the server and renders them:

var Chat = React.createClass({
  getInitialState: function() {
    return {
      messages: null
    }
  },
  componentWillMount: function(){
    var self = this;
    socket.emit('getMessages');
    socket.on('serverMessages', function (data) {
      self.setState({messages: data})
    });
  },
  render: function() {
    var messages = this.state.messages ? <MessageList messages={this.state.messages}/> : null
    return (
      <div className="jumbotron">
        { messages }
        <MessageForm submitMessage={this.submitMessage}/>
      </div>
      );
  }
});

Just in case here is my server code that emits data:

io.on('connection', function (socket) {

  socket.on('getMessages', function (data) {
    Message.find(function(err, messages){
      socket.emit('serverMessages', messages);
    })
  });

});
like image 811
fresh5447 Avatar asked Apr 15 '16 05:04

fresh5447


4 Answers

As of right now, you're "just" grabbing data from the server once the component has been loaded. To get something a bit more "real time" you'll want to either ping the server with the same emit statement you specified regularly (which defeats the point of using websockets, really, you could use long-polling) or have the server regularly send new data to all clients.

You can do EITHER:

A) Client side: "Polling" for information [Not Ideal]

Note: I initially put this in my answer because I saw the OP was "polling" when the controller was loaded. I didn't click on that this might be because the controller may not be loaded with the websocket so sending data on connect might not work here. My bad.

Replace socket.emit('getMessages') with something that will "poll" the websocket regularly for data:

setInterval(function () {
    socket.emit('getMessages')
}, 10000); /* Request data from the socket every 10 seconds */

OR

B) Server side: Send new data as it becomes available. [Best Way]

Track all clients via a clients array and delete them from it when their session ends.

var clients = [];

io.on('connection', function (socket) {
    clients.push(socket);

    socket.on('end', function () {
        // Could also splice the array below, but it still works.
        delete clients[clients.indexOf(socket)];
    });


    /* Previous logic for server goes here */
});

Run this code when you need to push new messages from the database/data storage:

for (var i in clients) {
    clients[i].emit('serverMessages', /* messages object */);
}
like image 195
megubyte Avatar answered Oct 22 '22 02:10

megubyte


Your server code is only firing upon initial socket connection.

Server:

socket.on('getMessages', function (data) {
  Message.find(function(err, messages){
    socket.emit('serverMessages', messages);
  })
});

Client:

var Chat = React.createClass({
  getInitialState: function() {
    return {
      messages: null
    }
  },
  componentWillMount: function(){
    var self = this;
    socket.emit('getMessages');
    socket.on('serverMessages', function (data) {
      self.setState({messages: data})
    });
  },
  render: function() {
    var messages = this.state.messages ? <MessageList messages={this.state.messages}/> : null
    return (
      <div className="jumbotron">
        { messages }
      </div>
      );
  }
});

Based on naming convention, it also appears that your Message.find() is pulling a single message. I would recommend clarifying the labeling to match cardinality.

like image 28
kwcto Avatar answered Oct 22 '22 02:10

kwcto


Try this:

var Chat = React.createClass({
  getInitialState: function() {
    return {
      messages: null
    }
  },
  componentWillMount: function(){
    var self = this;
    socket.emit('getMessages');
    socket.on('serverMessages', function (data) {
      self.setState({messages: data})
    });
  },
  render: function() {
    var messages = this.state.messages ? <MessageList messages={this.state.messages}/> : null
    return (
      <div className="jumbotron">
        { messages }
        <MessageForm submitMessage={this.submitMessage}/>
      </div>
      );
  }
});
like image 34
dwww Avatar answered Oct 22 '22 02:10

dwww


Could it be possible its due to the componentWillMount lifecycle method? Could you try the componentDidMount instead.

It looks like render will see the state update but only gets executed once despite the state change according to facebook.

like image 26
Jack7 Avatar answered Oct 22 '22 00:10

Jack7