最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - How can I fetch data from a Websocket in React? - Stack Overflow

programmeradmin4浏览0评论

I am trying to get realtime data from bitstamp API and store in my orders state. The page gets stuck in a loop and drains resources and react doesn't re-render the page when the orders state change. I can see the data if I log it to the console

This is what I have implemented so far.

  const [loading, setLoading] = useState(true);
  const [orders, setOrders] = useState([]);
  const [subscription, setSubscription] = useState({
    event: 'bts:subscribe',
    data: {
      channel: 'order_book_btcusd'
    }
  });
  const ws = new WebSocket('wss://ws.bitstamp');
  const initWebsocket = () => {
    ws.onopen = () => {
      ws.send(JSON.stringify(subscription));
    };
    ws.onmessage = (event) => {
      const response = JSON.parse(event.data);
      switch (response.event) {
        case 'data':
          setOrders(response.data);
          setLoading(false);
          break;
        case 'bts:request_reconnect':
          initWebsocket();
          break;
        default:
          break;
      }
    };
    ws.onclose = () => {
      initWebsocket();
    };
  };

  useEffect(() => {
    initWebsocket();
  }, [orders, subscription]);

  console.log(orders);

  const showResult = () => {
    orders.bids.map((el, index) => (
      <tr key={index}>
        <td> {el[0]} </td>
        <td> {el[1]} </td>
      </tr>
    ));
  };

I am trying to get realtime data from bitstamp API and store in my orders state. The page gets stuck in a loop and drains resources and react doesn't re-render the page when the orders state change. I can see the data if I log it to the console

This is what I have implemented so far.

  const [loading, setLoading] = useState(true);
  const [orders, setOrders] = useState([]);
  const [subscription, setSubscription] = useState({
    event: 'bts:subscribe',
    data: {
      channel: 'order_book_btcusd'
    }
  });
  const ws = new WebSocket('wss://ws.bitstamp');
  const initWebsocket = () => {
    ws.onopen = () => {
      ws.send(JSON.stringify(subscription));
    };
    ws.onmessage = (event) => {
      const response = JSON.parse(event.data);
      switch (response.event) {
        case 'data':
          setOrders(response.data);
          setLoading(false);
          break;
        case 'bts:request_reconnect':
          initWebsocket();
          break;
        default:
          break;
      }
    };
    ws.onclose = () => {
      initWebsocket();
    };
  };

  useEffect(() => {
    initWebsocket();
  }, [orders, subscription]);

  console.log(orders);

  const showResult = () => {
    orders.bids.map((el, index) => (
      <tr key={index}>
        <td> {el[0]} </td>
        <td> {el[1]} </td>
      </tr>
    ));
  };

Share edited Sep 29, 2019 at 21:08 SAMA BALA asked Sep 29, 2019 at 14:06 SAMA BALASAMA BALA 713 silver badges11 bronze badges
Add a ment  | 

2 Answers 2

Reset to default 3

This is happening because useEffect execute its callback after each render cycle i.e it runs both after the first render and after every update. So for every first message received it is opening a new WebSocket connection and storing the data in the state which is causing a loop.

You can read more about useEffect here

Edited:-

useEffect(() => {
    initWebsocket();
}, [orders, subscription]);

The optional second argument to useEffect is used to detect if anything has changed or not (basically it pares prev state/props and given state/props) and it calls the effect whenever there is a change in value.

So on every orders state update, this effect will get called and which in turn causes a loop.

Solution:-

But in your case, you want to establish WebSocket connection only once after the ponent has mounted and keep listening to the ining data irrespective of any state or prop change. You can pass an empty [] such that it gets called only once on mount and unmount.

useEffect(() => {
    initWebsocket();
    // cleanup method which will be called before next execution. in your case unmount.
    return () => {
      ws.close
    }
}, []);

From doc:-

This requirement is mon enough that it is built into the useEffect Hook API. You can tell React to skip applying an effect if certain values haven’t changed between re-renders. To do so, pass an array as an optional second argument to useEffect.

If you want to run an effect and clean it up only once (on mount and unmount), you can pass an empty array ([]) as a second argument. This tells React that your effect doesn’t depend on any values from props or state, so it never needs to re-run. This isn’t handled as a special case — it follows directly from how the dependencies array always works.

If you pass an empty array ([]), the props and state inside the effect will always have their initial values. While passing [] as the second argument is closer to the familiar ponentDidMount and ponentWillUnmount mental model, there are usually better solutions to avoid re-running effects too often.

In useEffect, check if the WebSocket connection is closed before initializing it.

If you are confused with the working of react hooks, you can use class ponents and initialize your WebSocket connection in ponentDidMount and ponentDidUpdate(Check if the connection is closed and initialize it).

PS: I have implemented a simple Chat Application using React and WebSockets.

https://github./Nikhil-Kumaran/ChatApp

Go through the repo to have a better idea.

Related ponent: https://github./Nikhil-Kumaran/ChatApp/blob/master/src/WebSockets.js

发布评论

评论列表(0)

  1. 暂无评论