如何在 next.js 中显示来自 websockets 的数据并做出反应?

时间:2021-03-24 02:46:42

标签: reactjs websocket next.js

我正在使用 Websockets,但无法弄清楚如何实际显示数据。我是 JS 和 React 的新手,不习惯新的做事方式,特别是因为它在不断变化。

我在 React 中使用 Next.js。这是我运行服务器端的 _app.js 文件。

当我在 onmessage 内使用 setMessages(result) 时,我没有收到任何错误。但我在尝试使用该状态时确实收到错误消息。

我基本上是在尝试显示来自正在连接和工作的 websocket 的数据。正在返回数据。我只需要以某种方式减缓数据流并将其输入到我的组件中。

getApiAndEmit 运行的唯一时间是连接关闭时,并且数据无法返回。控制台登录 client.onclose 显示在前端,所以我想这可能是我将数据返回给客户端的时间。

我想了解做到这一点的最佳方法。并感谢您的帮助。

_app.js

import React, { useEffect, useState } from 'react';
const cors = require('cors');

import '../styles/_variables.scss'
import '../styles/_all.scss'
import '../node_modules/react-vis/dist/style.css';
import {w3cwebsocket as W3CWebSocket} from "websocket/lib/websocket"

// App currently does not support Next.js Data Fetching methods like getStaticProps or getServerSideProps.
function MyApp({ Component, pageProps }) {


  const [messageText, setMessageText] = useState("");
  const [receivedMessages, setMessages] = useState([]);
  const messageTextIsEmpty = messageText.trim().length === 0;

  const client = new W3CWebSocket('wss://ws-feed.pro.coinbase.com');

  const channel = {
    "type": "subscribe",
    "product_ids": [
      "BTC-USD",
      "BTC-EUR"
    ],
    "channels": [
      "heartbeat",
      "level2",
      {
        "name": "ticker",
        "product_ids": [
          "BTC-USD"
        ]
      }
    ]
  }

  let interval;

  const getApiAndEmit = (type,result) => {
    //const response = new Date();
    // Emitting a new message. Will be consumed by the client

    console.log("getApiAndEmit");
    console.log(result)
    //socket.emit("FromAPI", response);
  };


  // shows up in front end console.
  client.onclose = (socket) => {

    console.log("onclose socket cleear interveal");
    console.log(socket);

    if (interval) {
      clearInterval(interval);
    }
    interval = setInterval(() => getApiAndEmit("onclose","onclose"), 5000);

  }

  // TODO: flooding, constantly opening?
  client.onopen = (socket) => {
    console.log("OPEN intetval")
    if (interval) {
      console.log("OPEN clearInterval")
      clearInterval(interval);
    }
    interval = setInterval(() => getApiAndEmit("OPEN "," OPEN "), 5000);

    // clear interval didn't work well here
    client.send(JSON.stringify(channel));
  };

  // runs millions of times
  client.onmessage = (message) => {

    // filter updates to products, not connections, etc
    const result = JSON.parse(message.data)
    const type = result.type;

    switch(type) {
      case 'subscriptions':

        break;
      case 'ticker':

        // runs server side!
        //console.log("setting message",result)
        if (interval) {
          console.log("CLEAR interval")
          clearInterval(interval);
        } else {
          console.log("DO NOT CLEAR interval")
          interval = setInterval(() => getApiAndEmit(type,result), 5000);
        }


        setMessages(result)
        // old way
        //this.setState({dataFromServer: result})
        //const [count, setCount] = useState(0);
        break;
      default:
      // code block
    }



    /*

    response
  {
  "type": "ticker",
  "sequence": 3074716831,
  "product_id": "ETH-BTC",
  "price": "0.03076",
  "open_24h": "0.03111",
  "volume_24h": "14209.34102156",
  "low_24h": "0.03057",
  "high_24h": "0.03143",
  "volume_30d": "824694.85566290",
  "best_bid": "0.03076",
  "best_ask": "0.03077",
  "side": "sell",
  "time": "2021-03-22T21:34:21.464755Z",
  "trade_id": 14723995,
  "last_size": "2.19802296"
  }
     */

  };

  return <Component {...pageProps} />
}

export default MyApp

我的组件在尝试访问状态时收到错误消息

import Link from 'next/link'
import useSwr from "swr"
import {useRouter} from "next/router"
import React, { useState, useEffect } from 'react';


const SimpleTicker = (props) => {

  /*
  function dispatchAction(fiber, queue, action) {
  {
    if (typeof arguments[3] === 'function') {
      error("State updates from the useState()
      and useReducer() Hooks don't support the " + 'second callback argument.
       To execute a side effect after ' + 'rendering, declare it in the component body with useEffect().');
    }
    */
  //const [messageText, setMessageText] = useState("");
  //const [receivedMessages, setMessages] = useState([]);


  return (
    <div className={`ticker ticker-${props.crypto}`}>
      <div>${props.crypto}</div>
      {/*<span>{data.ticker.price}</span>*/}
    </div>
  );
};

export default SimpleTicker;

0 个答案:

没有答案