简体   繁体   中英

SSE - Server Sent Events - How to reuse same EventSource

I'm new in programming and trying to understand SSE. I'm using React for frontend, Node.js for backend and MongoDB for database. Sorry for my english in advance.

When i open the website, creating eventsource and start to listen backend "/test". I have a button in frontend and when i click it, a random float number between 0 and 1 posted backend "/savedata". Also showing numbers bigger than 0.5.

In server side for "/test", checking database for the new records every 3 seconds and if recorded numbers is bigger than 0.5, send it to frontend and delete the database record. For "/savedata", saving numbers to database coming from frontend.

My Question is;

when i open website in a new tab, another eventsource is being created and trigger database "/test". Is there a way to reuse eventsource instead of create a new eventsource? Also if you have any suggestions for my code, pls tell me. i'm trying to learn.

Here is my code;

Frontend React - FrontendTest.js

import axios from 'axios'
import React, { useEffect, useState } from 'react'


const FrontendTest = () => {
 const [data, setData] = useState(null)
 const [databaseInfo, setDatabaseInfo] = useState(null)


 let number = 0
 const url = 'http://localhost:5000/test'
 let source

 useEffect(() => {
    source = new EventSource(url)
    source.onmessage = (e) => {
    setData(JSON.parse(e.data))
    }
  }, [])

 const buttonClicked = async (e) => {
    e.preventDefault()
    number = Math.random()
    const sendReq = await 
 axios.post('http://localhost:5000/savedata', {
      number,
    })
    setDatabaseInfo(sendReq.data)
  }

  return (
    <div>
      <div>
        <button onClick={buttonClicked}>Send</button>
        <p>{`If the number > 0.5 , it will be founded`}</p>
        <p>
          {databaseInfo &&
            `${databaseInfo.data.toFixed(4)} Saved to 
 Database !`}
        </p>
        <p>
          {data && `${data.toFixed(4)} Founded ! Database 
 input deleted !   `}
        </p>
      </div>
     </div>
  )
}

Node.js - server.js

import express from 'express'
import cors from 'cors'
import expressAsyncHandler from 'express-async-handler'
import mongoose from 'mongoose'
import Datas from './model.js'


const app = express()
const port = 5000

app.use(
  cors({
    origin: 'http://localhost:3000',
    credentials: true,
  })
)
app.use(express.json())
app.use(express.urlencoded({ extended: true }))

let interval

app.post(
  '/savedata',
  expressAsyncHandler(async (req, res) => {
    const data = req.body.number
    const result = await Datas.create({
      data1: data,
    })
    res.send({ data })
  })
)

app.get(
  '/test',
  expressAsyncHandler(async (req, res) => {
    res.setHeader('Content-Type', 'text/event-stream')
    res.setHeader('Cache-Control', 'no-cache')
    clearInterval(interval)
    interval = setInterval(async () => {
      const database = await Datas.find({})
      const databaseData1 = database.map((item) => item.data1)
      const databaseIds = database.map((item) => item._id)
      const data = {
        value: databaseData1,
      }

      for (let i = 0; i < data.value.length; i++) {
        if (data.value[i] > 0.5) {
          console.log(data.value[i])
          res.write(`data: ${JSON.stringify(data.value[i])}\n\n`)
          await Datas.findByIdAndDelete(databaseIds[i])
        }
      }
      console.log('Searching')
    }, 3000)
  })
)

mongoose
  .connect(CONNECTION_URL, { useNewUrlParser: true, useUnifiedTopology: true })
  .then(() =>
    app.listen(port, () =>
      console.log(`Example app listening at
    http://localhost:${port}`)
    )
  )
  .catch((error) => console.log(error))

You can't directly share the event source handle, but what you are able to do is share data between tabs, when they are in the same origin.

One approach would be LocalStorage. So the first tab would write to the storage that it is going to run the EventSource connection. The second tab that connects would see there is already another tab open, and start listening. Then each time an event comes into the first tab, it writes it to local storage, and the second tab can see it.

(You need to handle the case of what happens if the first tab closes; especially if there are multiple other tabs listening in, so be aware that making this production-ready is going to get quite complicated.)

Another WebAPI that is specifically for doing that kind of thing is Broadcast Channel . I've not used it: the browser support is almost the same as EventSource, but Safari is lagging behind.

It looks like this question was created to keep track of the various approaches.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM