Fig. Server sends events to the frontend client. (edit)
In the traditional way, the scenario is a browser pulls server resources by sending the Requests. However, what if, we want to develop a web app that the server can send your browser unlimited amounts of information until you leave that page. The web app opens a connection to the server, listening to the message from it, renders the update. Here I'll show you how to do.
The most likely cause of the eventsource requests failing is that they are timing out. The Chrome browser will kill an inactive stream after two minutes of inactivity. If you want to keep it alive, you need to add some code to send something from the server to the browser at least once every two minutes. Just to be safe, it is probably best to send something every minute. An example of what you need is below. It should do what you need if you add it after your second setTimeout in your server code.
const intervalId = setInterval(() => {
res.write(`data: keep connection alive\n\n`);
res.flush();
}, 60 * 1000);
req.on('close', () => {
// Make sure to clean up after yourself when the connection is closed
On the server side, just respond to the MIME content type as text/event-stream and use the "write" method to send the UTF-8 based string to the client side. Remember that, by default, if the connection between the client and server closes, the connection is restarted. The connection is terminated with the .close() method [ref].
// Note: I customized the data format sent from server: {num: value}
const strNum:string = JSON.parse(event.data).num; // parsing the data
let num:number = parseInt(strNum);
console.log('num = ' + num);
}
By default, if the connection between the client and server closes, the connection is restarted. The connection is terminated with the .close() method[2].
3.3. Stop
// Step 3: Asking server stop pushing data
evtSource.close();
3.4. Code
File: src/App.tsx
import React, { useState } from 'react';
import './App.css';
import Button from '@material-ui/core/Button';
import MyImage from './component/my-image';
function App() {
// Create the Ref to your component
let myimage = React.createRef<MyImage>();
const onStartSentEventFromServer = async () => {
console.log('onStartSentEventFromServer')
// const url = 'http://192.168.1.100:9000/statusAPI'; // ok
const url = 'statusAPI'; // ok
//const evtSource = new EventSource(url, { withCredentials: true } ); // ok
// Step 1: Create connection
const evtSource = new EventSource(url); // ok
// Step 2: Receiving events from the server
evtSource.onmessage = function(event) {
const strNum:string = JSON.parse(event.data).num;
let num:number = parseInt(strNum);
console.log('num = ' + num);
(myimage.current as MyImage).changeLight(num%2);
/* By default,
if the connection between the client and server closes, the connection is restarted. The connection is terminated with the .close() method.
*/
if (num == 5){
// Step 3: Asking server stop pushing data
console.log('[Early Stop] entSource.close')
evtSource.close();
}
}
}
return (
<div className="App">
<header className="App-header">
<p>
<MyImage ref = {myimage} />
<Button onClick={onStartSentEventFromServer} variant="contained" color="primary"> Start to received event from Server </Button>
</p>
</header>
</div>
);
}
export default App;
For the image component, I borrow the code from here ( [react, component, image] How to switch the image when user clicked the button) that can switch the image based on the method chageLight.