Skip to content

Instantly share code, notes, and snippets.

@Mosharush
Last active January 31, 2024 12:25
Show Gist options
  • Save Mosharush/8bbc178bbc7e47c7c7c554dd7b5c5528 to your computer and use it in GitHub Desktop.
Save Mosharush/8bbc178bbc7e47c7c7c554dd7b5c5528 to your computer and use it in GitHub Desktop.
React Hook - Server-Sent Events (SSE)

Server-Sent Events (SSE) are commonly used in real-time applications where the server needs to push updates to the client.

Here are a few use cases:

  1. Real-time notifications: SSE can be used to push notifications to the client in real-time. For example, in a social media application, the server can push notifications about new posts, likes, comments, etc.
  2. Live news updates: In a news application, the server can push live news updates to the client.
  3. Real-time analytics: In an analytics dashboard, the server can push real-time data updates to the client.
  4. Chat applications: In a chat application, the server can push new messages to the client in real-time.
  5. Online multiplayer games: In an online multiplayer game, the server can push game state updates to the client in real-time.
  6. Stock price updates: In a stock trading application, the server can push real-time stock price updates to the client.

useSse - Server-Sent Events Hook

useSse is a custom React hook for handling Server-Sent Events (SSE) in your application.

Usage

Here's a basic example of how to use the useSse hook:

import useSse from './useSse';

const MyComponent = () => {
  const { connectionState, connectionError, addListener, getEventData, closeConnection } = useSse('https://my-api.com/events', {
    withCredentials: true
  });

  // Add event listener
  addListener('myEvent', (data) => {
    console.log('Received data:', data);
  });

  // Get event data
  const [data, setData] = getEventData('myEvent');
  console.log('Event data:', data);

  // Close connection
  closeConnection();

  return (
    <div>
      <p>Connection State: {connectionState}</p>
      {connectionError && <p>Error: {connectionError.message}</p>}
    </div>
  );
};

API

The useSse hook returns an object with the following properties:

  • connectionState: The current state of the SSE connection. Possible values are 'CONNECTING', 'OPEN', and 'CLOSED'.
  • connectionError: Any error that occurred while connecting to the SSE server.
  • addListener(eventName, eventHandler): A function to add an event listener for a specific event. The eventHandler function will be called with the data from the event.
  • getEventData(eventName): A function to get the data for a specific event. It returns a state variable and a setter function, similar to how useState works. The state variable is initialized with the current event data, and it's updated whenever the event data changes. This makes getEventData reactive, as changes to the event data will cause components using this data to re-render.
  • closeConnection(): A function to close the SSE connection.
const express = require('express');
const SSE = require('sse-stream');
const app = express();
const sse = SSE();
app.get('/events', sse, (req, res) => {
setInterval(() => {
const data = Math.random().toString(); // Generate random data
res.sse.event('MyEvent', data);
}, 1000);
});
app.listen(3000, () => {
console.log('Server is running on port 3000');
});
import { useEffect, useState, useCallback } from 'react';
const useSse = (url, options) => {
const [connectionState, setConnectionState] = useState('CONNECTING');
const [connectionError, setConnectionError] = useState(null);
const [eventSource, setEventSource] = useState(null);
const [eventData, setEventData] = useState({});
useEffect(() => {
const es = new EventSource(url, options);
setEventSource(es);
es.onopen = () => setConnectionState('OPEN');
es.onerror = (error) => {
setConnectionState('CLOSED');
setConnectionError(error);
};
return () => es.close();
}, [url, options]);
const addListener = useCallback((eventName, eventHandler) => {
if (eventSource) {
eventSource.addEventListener(eventName, (event) => {
setEventData((prevData) => ({
...prevData,
[eventName]: event.data,
}));
eventHandler(event.data);
});
}
}, [eventSource]);
const getEventData = useCallback((eventName) => {
const [data, setData] = useState(eventData[eventName]);
useEffect(() => {
setData(eventData[eventName]);
}, [eventData[eventName]]);
return [data, setData];
}, [eventData]);
const closeConnection = useCallback(() => eventSource?.close(), [eventSource]);
return { connectionState, connectionError, addListener, getEventData, closeConnection };
};
export default useSse;
import { useEffect, useState, useCallback } from 'react';
interface EventData {
[key: string]: string;
}
const useSse = (url: string, options?: EventSourceInit) => {
const [connectionState, setConnectionState] = useState<string>('CONNECTING');
const [connectionError, setConnectionError] = useState<Event | null>(null);
const [eventSource, setEventSource] = useState<EventSource | null>(null);
const [eventData, setEventData] = useState<EventData>({});
useEffect(() => {
const es = new EventSource(url, options);
setEventSource(es);
es.onopen = () => setConnectionState('OPEN');
es.onerror = (error: Event) => {
setConnectionState('CLOSED');
setConnectionError(error);
};
return () => es.close();
}, [url, options]);
const addListener = useCallback((eventName: string, eventHandler: (data: string) => void) => {
if (eventSource) {
eventSource.addEventListener(eventName, (event: MessageEvent) => {
setEventData((prevData) => ({
...prevData,
[eventName]: event.data,
}));
eventHandler(event.data);
});
}
}, [eventSource]);
const getEventData = useCallback((eventName: string) => {
const [data, setData] = useState<string | undefined>(eventData[eventName]);
useEffect(() => {
setData(eventData[eventName]);
}, [eventData[eventName]]);
return [data, setData];
}, [eventData]);
const closeConnection = useCallback(() => eventSource?.close(), [eventSource]);
return { connectionState, connectionError, addListener, getEventData, closeConnection };
};
export default useSse;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment