23
Real-Time Interactive Plotting Client-Side (using Sockets, React Hooks & Plotly)
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
I'll be making use of a few frameworks and libraries along the way, most importantly:
- React
- react-plotly.js (a wrapper based around plotly)
- socket.io-client
First we need to setup a development environment using create-react-app
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
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
npm start
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 file and declare it as REACT_APP_SOCKET_URL
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.
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)
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!
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.
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
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
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
.
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.
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
return (
<div className="wrapper">
<Plot
{...data}
/>
)}
</div>
)
We use the spread operator 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
return (
<div className="wrapper">
{loading?(
<p>
loading
</p>
):(
<Plot
{...data}
/>
)}
</div>
)
Now that we have everything in place, this is what it should look like:
Adding some basic styling to the body and making the graph cover the entire screen
This is how the end result should look like
Link to Github Repository
Cover Photo by Tech Daily on Unsplash
23