Alexander Schaaf

How to use WebSockets in React

A web app updates its state by requesting data from a server using HTTP requests. We do this using the Fetch API of a browser. But only the client can request an update of data from the server. The server itself can’t push updates to a client by itself.

One way to solve this is by using the browsers WebSocket API. It provides a two-way communication channel between a server and a client. This allows the server to send messages to the client, and vice versa.

What is a WebSocket?

An HTTP request is a one-time connection between server and client. It involves a request from the browser and a response from the server. A WebSocket is different. It’s a persistent connection between server and client, which allows communication in both directions. This is often called a full-duplex connection, like a phone line which allows both parties to talk at the same time.

Setting up a WebSocket Server

I used ws to create a tiny NodeJS WebSocket server in just a few lines of code. To do that install ws using yarn add ws and create the file server.js. In it we create a WebSocketServer serving on port 8080. I want the server to regularly send out a temperature reading to the client, which can then display it to the user. When a WebSocket connection is made, the server sends a message to the connected client every second. In this example we can simply generate some random temperature data and timestamp it.

const { WebSocketServer } = require('ws')

const server = new WebSocketServer({ port: 8080 })

const getTemperatureReading = () => {
  return Math.random() * 5 + 18 // generate a random temperature
}

server.on('connection', (ws) => {
  setInterval(() => {
    ws.send(
      JSON.stringify({
        temperature: getTemperatureReading()
        timestamp: new Date(), // current timestamp
      })
    )
  }, 1000) // 1000 milisecond interval
})

That’s all we need to do. Start the server by running node server.js.

How to use a WebSocket in React

Now that the server is ready, we can consume the WebSocket data using a React app. I quickly set up a React app using Vite in which to use the following component.

Create a ServerTemperature component with temperatureData state variable. If the data is undefined, we render that no temperature data is available. If we do have data, we render the temperature data.

import React, { useEffect, useState } from "react";

interface TemperatureData {
  amount: number;
  timestamp: Date;
}

const ServerTemperature: React.FC = () => {
  const [temperatureData, setTemperatureData] = useState<TemperatureData>();

  if (temperatureData === undefined) {
    return <p>No temperature data.</p>;
  }

  return <p>{temperatureData.temperature.toFixed(1)} °C</p>;
};

Now its time to actually establish a WebSocket connection! To do so lets use JavaScripts inbuilt WebSockets API. It’s important to only do this once, and not on every re-render. A useEffect Hook is perfect for that. Specifying an empty dependency array [] makes sure to only establish the connection when the component mounts.

useEffect(() => {
  const webSocket = new WebSocket("ws://localhost:8000/temperature");
}, []);

We now have a WebSocket connection - but we still need to define what should happen when we receive a message from the server. For that we add an onmessage callback function to the instantiated WebSocket. It parses the message JSON data and updates our state.

webSocket.onmessage = (event: MessageEvent) => {
  const data = JSON.parse(event.data);
  setTemperatureData({
    amount: data.amount,
    timestamp: new Date(data.timestamp),
  });
};

Now run the React app and you will see that the component will update whenever it receives a new message from the server - every second.

You can find the entirety of the code on my GitHub.