将WebSockets与ReactJS一起使用时,不知道如何在客户端上呈现服务器消息

时间:2018-03-22 03:35:15

标签: javascript reactjs websocket socket.io

我正在尝试将Socket.IO与我的应用程序集成。我正在构建一个实时数据工具。现在,我只想看到我的服务器和客户端之间的持续通信。我想发送一条简单的消息,并每隔5秒在控制台上记录一次。

这是我的server.js:

const path = require('path');
const express = require('express');
const http = require("http");
const socketIo = require("socket.io");

const DIST_DIR = path.join(__dirname, 'public/');
const PORT = 3000;

const app = express();
app.use(express.static(DIST_DIR));

const server = http.createServer(app);

//wire up ExpressJS server to Socket.IO
const io = socketIo(server);

io.on("connection", (socket) => {
  console.log("Connected to client"), setInterval(
    () => socket.emit('testEvent', "Message from server sent"),
    5000
  );
  socket.on("disconnect", () => console.log("Client disconnected"));
});

app.get('*', function(req, res) {
    res.sendFile(path.join(DIST_DIR, "index.html"));
});

server.listen(PORT, () => console.log("server listening on port " + PORT));

在客户端上,我(我正在使用DataMaps jQuery插件):

import propTypes from 'prop-types';
import React, { Component } from 'react';
import Datamaps from 'datamaps';
import socketIOClient from "socket.io-client";

export default class Datamap extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      response: false,
      endpoint: "http://127.0.0.1:3000"
    };
    window.addEventListener('resize', this.resize);
  }

  resize() {
    if (this.map) {
      this.map.resize();
    }
  }

  //creates a new connection to Socket.IO server
  componentDidMount() {
    const { endpoint } = this.state;
    const socket = socketIOClient(endpoint);
    socket.on("testEvent", data => this.setState({ response: data }));
    this.drawMap();
  }

  componentWillReceiveProps() {
    this.clear();
  }

  componentDidUpdate() {
    this.drawMap();
  }

  componentWillUnmount() {
    this.clear();
    window.removeEventListener('resize', this.resize);
  }

  clear() {
    const container = this.refs.container;

    for (const child of Array.from(container.childNodes)) {
      container.removeChild(child);
    }
  }

  fadingBubbles(layer, data) {
       //irrelevant, left out
  }


  drawMap() {
    var map = new Datamaps({
      ...this.props,
      element: this.refs.container,
      projection: 'mercator',
      responsive: true
    });

    if (this.props.arc) {
      map.arc(this.props.arc, this.props.arcOptions);
    }

    if (this.props.bubbles) {
      map.bubbles(this.props.bubbles, this.props.bubbleOptions);
    }

    if (this.props.graticule) {
      map.graticule();
    }

    if (this.props.labels) {
      map.labels();
    }

    this.map = map;
    this.map.addPlugin('fadingBubbles', this.fadingBubbles.bind(this.map));
  }

  drawBubbles = () => {
    const { response } = this.state;
    var data = [
      {
        "latitude": "28.014067",
        "longitude": "-81.728676"
      }, {
        "latitude": "40.750793",
        "longitude": "-73.989525",
        "magnitude": 3
      }
    ];
    this.map.fadingBubbles(data);
    //console.log({ response });
  }

  render() {
    const style = {
      position: 'relative',
      width: '100%',
      height: '100%'
    };

    return (<div>
      <button onClick={this.drawBubbles}>Draw Fading Bubbles</button>
      <div ref="container" style={style}></div>
    </div>);
  }
}

Datamap.propTypes = {
  arc: propTypes.array,
  arcOptions: propTypes.object,
  bubbleOptions: propTypes.object,
  bubbles: propTypes.array,
  graticule: propTypes.bool,
  labels: propTypes.bool
};

所以,目前我有一个按钮。最终函数drawBubbles将从服务器检索lat和long信息并将其提供给地图以显示相应的气泡,但是现在我只想要这些消息,我将能够弄明白。按下按钮时,消息将打印在客户端的控制台上,这是正确的。但是我想摆脱那个按钮,并且能够每5秒钟看到一次消息(所以没有onclick动作),就像在我的服务器中写的那样。我不完全确定组件安装是如何工作的,但是当我只将函数调用this.drawBubbles;放在render()函数内部的返回之外时,我得到了以下警告(并且没有在控制台上打印): / p>

Warning: Can only update a mounted or mounting component. This usually means you called setState, replaceState, or forceUpdate on an unmounted component. This is a no-op.

Please check the code for the Datamap component.

有人可以解释一下我是如何让这个工作的吗?也许组件在我的应用程序环境中如何工作可能对我有用。

1 个答案:

答案 0 :(得分:0)

我怀疑它正在运作,但你不喜欢警告你在卸载时更新状态(我也讨厌它,因为它暗示事情不按预期管理)。我喜欢componentWillUnmount()的这张便条:

  

在此方法中执行任何必要的清理,例如使计时器无效,取消网络请求或清理在componentDidMount()中创建的任何订阅。

也许考虑在自己的实例中初始化socket.io客户端,生活在Datamap之外,这样就可以将socket.io活动与Datamap组件的生命周期分离。

以下是您可以在Datamap实施中扩展的一个简单示例:

const socket = socketIOClient('http://127.0.0.1:3000');
let listener = null;
socket.on('testEvent', (data) => {
  if (listener) {
    listener.handleTestEvent({ response: data });
  }
});

export class Datamap extends React.Component {
  componentWillUnmount() {
    listener = null;
  }

  componentDidMount() {
    listener = {
      handleTestEvent: (ev) => console.log(ev.response),
    };
  }
}