1

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.net');
  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>
    ));
  };

2 Answers 2

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 compares 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 component has mounted and keep listening to the incoming 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 common 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 componentDidMount and componentWillUnmount mental model, there are usually better solutions to avoid re-running effects too often.

Sign up to request clarification or add additional context in comments.

1 Comment

How do I stop it from doing that? I couldn't get any clarifications on how to do that from the docs.
2

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 components and initialize your WebSocket connection in componentDidMount and componentDidUpdate(Check if the connection is closed and initialize it).

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

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

Go through the repo to have a better idea.

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

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.