Real-Time Interactive Plotting Client-Side (using Sockets, React Hooks & Plotly)
Tags: react, socket, plotly, graph
Published: 2021-07-09 20:05:50

Plotting real-time graphs might appear to be hard, especially if you don't know where to start. 
Fortunately socket.io makes this ridiculously easy to do, especially if the server is handling most of the hard work...

In this article, I will explain the client-side implementation of a real-time plotting system. This is a second part to the series, so if you haven't read the server-side implementation, check it out [here](https://dev.to/radioactive11/real-time-interactive-plotting-using-sockets-python-plotly-297g)

I'll be making use of a few frameworks and libraries along the way, most importantly:

* React
* [react-plotly.js](https://github.com/plotly/react-plotly.js) (a wrapper based around plotly)
* [socket.io-client](https://github.com/socketio/socket.io-client)


## Prerequisites
First we need to setup a development environment using [create-react-app](https://github.com/facebookincubator/create-react-app)

```bash
npx create-react-app real-time-plotting
```

After that, we need to cd into `real-time-plotting` and install a few additional libraries we need

```bash
cd real-time-plotting
npm install react-plotly.js plotly.js socket.io-client
```

We are good to go now! Start the development server using 

```bash
npm start
```

## Initialising a socket connection

We need to make sure that our client can establish a socket connection with the backend. For this we will be using the `socket.io-client` library. We also store our backend URL in an [env](https://create-react-app.dev/docs/adding-custom-environment-variables/) file and declare it as `REACT_APP_SOCKET_URL`

```js
import { io } from "socket.io-client";

const socketURL = process.env.REACT_APP_SOCKET_URL;
const socket = io(socketURL);
```

Now that we have a socket variable, we can listen to the on connect event and emit a graph request to the server.

```js
socket.on("connect",()=>{
	socket.emit("ping_graph", {symbol: "ril.ns"});
});
```

Great! Now the server should be sending us the graph data on the event called `graph-plot` (refer to server-side implementation if you want to know how this works)

```js
socket.on("graph_plot", res => {
	let response = JSON.parse(res);
});
```

We have the graph's data stored in the `response` variable now.
Its time we integrate this with React!

## Using React's [useEffect](https://reactjs.org/docs/hooks-effect.html) and [useState](https://reactjs.org/docs/hooks-state.html) Hooks

At first it might look a bit intimidating but `useState` is surprisingly easy to wrap your head around!
Its a function that returns a stateful value, and a function that updates it.

React's `useEffect` hook is used to run a particular function either after a complete render or when certain values get changed (by passing them in an array as a second argument)

This is going to be particularly handy since we need to ensure that our socket connection is established only once after the initial render. 

```jsx
const [data, setData] = useState(null);
const [loading, setLoading] = useState(true);

useEffect(() => {
		const socketURL = process.env.REACT_APP_SOCKET_URL;
		const socket = io(socketURL);
		socket.on("connect",()=>{
			socket.emit("ping_graph", {symbol: "ril.ns"});
		});
		socket.on("graph_plot", res => {
			if(loading===true){
				setLoading(false);
			}
			let response = JSON.parse(res);
			response.config = {responsive: true}
			setData(response);
		});
		return () => socket.disconnect();
}, []);
```

As you see, a couple of things happened here

```jsx
const [data, setData] = useState(null);
const [loading, setLoading] = useState(true);
```

This basically sets two stateful variables `loading` and `data` where data is intially set to `null` and loading is sent to `true`

Inside the socket event listener for `graph_plot`, we do two important things

```jsx
if(loading===true){
	setLoading(false);
}
setData(response);
```
The first statement is essentially an `if` statement that sets `loading` state as false when it runs for the first time

The second `setData` assigns the socket value we just got as the `data`

and lastly, we added a return statement inside `useEffect`.
```jsx
return () => socket.disconnect();
```
This is known as a cleanup statement and is done to ensure that the socket connection is closed when the component is unmounted so we don't accidentally introduce memory leaks.

## Plotting data using Plotly's React Component

This is the easiest step so far as it simply involves you to create a Plot with the data we get from the server.
Its as easy as 
```jsx
return (
	<div className="wrapper">
		<Plot
			{...data}
		/>
	)}
	</div>
)
```
We use the [spread operator](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Spread_syntax) to pass the data we got back as an object as props for the `Plot` component.
Now to make sure we don't load an empty graph before we actually get the data back from the server, we use the `loading` variable as the statement to a [conditional operator](https://reactjs.org/docs/conditional-rendering.html#inline-if-else-with-conditional-operator)
```jsx
return (
	<div className="wrapper">
		{loading?(
			<p>
				loading
			</p>
		):(
			<Plot
				{...data}
			/>
		)}
	</div>
)
```
## Finishing up

Now that we have everything in place, this is what it should look like:

{% gist https://gist.github.com/Boidushya/12ee205132a78dcb7448dfd07108644a  %}
Adding some basic styling to the body and making the graph cover the entire screen
{% gist https://gist.github.com/Boidushya/f96acf07c1838197e5689d38fa2535d1
%}

This is how the end result should look like

![Graph Demo](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/i4bimmgpznksij5i6k8i.gif)

## Resources

> Link to [Github Repository](https://github.com/radioactive11/real-time-plot)
> Cover Photo by [Tech Daily](https://unsplash.com/@techdailyca) on [Unsplash](https://unsplash.com/)