react-native-sse

repository·master·Indexed 18 days ago

https://github.com/binaryminds/react-native-sse

An EventSource implementation for React Native that provides Server-Sent Events (SSE) for iOS and Android. It uses XMLHttpRequest to enable real-time data streaming without requiring custom native modules. The library supports custom headers, POST requests for services like OpenAI streaming, and provides event listeners for open, message, error, done, and close states.

Tokens
4.5K
Snippets
11
Records
14
Agent score
63%

What's inside react-native-sse

  1. Understand the difference between 'done' and 'close' events

    master

    It is important to distinguish between how a connection ends:

    • done: Fired when the server closes the connection. By default, the client will automatically attempt to reconnect. To disable this automatic reconnection, set the pollingInterval option to 0.
    • close: Fired when the client terminates the connection explicitly by calling the .close() method.
  2. Manage SSE connection lifecycle with AppState

    master

    By default, react-native-sse does not automatically close connections when the app enters the background/inactive state, nor does it automatically reconnect when the app returns to the foreground. Additionally, timeouts may be killed while the app is in sleep mode.

    To save resources and ensure the connection is re-established when the user returns to your app, use the React Native AppState API to manually call .open() and .close() on your EventSource instance.

    import { useEffect } from 'react';
    import { AppState } from 'react-native';
    
    // Assuming `es` is your EventSource instance
    
    useEffect(() => {
      const appStateSubscription = AppState.addEventListener('change', (nextAppState) => {
        if (nextAppState === 'active') {
          // App became active, reconnect SSE
          es.open();
        } else if (nextAppState === 'background' || nextAppState === 'inactive') {
          // App went to background, close SSE connection
          es.close();
        }
      });
    
      return () => {
        appStateSubscription.remove();
      };
    }, [es]);
  3. Install react-native-sse

    master

    Install the library using your preferred package manager. The library uses XMLHttpRequest to handle SSE connections, so no additional native Android or iOS implementation is required.

    # Using Yarn
    yarn add react-native-sse
    
    # Using NPM
    npm install --save react-native-sse
  4. Configure EventSource with headers and parameters

    master

    You can pass an options object to the EventSource constructor to include custom headers (like Bearer tokens) and URL search parameters. When using URL objects in React Native, it is recommended to use react-native-url-polyfill/auto.

    import React, { useEffect, useState } from "react";
    import { View, Text } from "react-native";
    import EventSource, { EventSourceListener } from "react-native-sse";
    import "react-native-url-polyfill/auto";
    
    // ... inside a component
    useEffect(() => {
      const url = new URL("https://your-sse-server.com/.well-known/mercure");
      url.searchParams.append("topic", "/book/{bookId}");
    
      const es = new EventSource(url, {
        headers: {
          Authorization: {
            toString: function () {
              return "Bearer " + token;
            },
          },
        },
      });
    
      // ... add listeners
    
      return () => {
        es.removeAllEventListeners();
        es.close();
      };
    }, []);
  5. Understand the difference between 'done' and 'close'

    master

    The EventSource class distinguishes between a stream finishing naturally and a connection being manually terminated:

    • done event: Emitted when the server completes the response (e.g., readyState becomes DONE). The library will automatically attempt to poll again after the configured interval.
    • close event: Emitted when the .close() method is explicitly called by the user. This stops all polling and aborts the underlying XHR request.
  6. Use EventSource with ChatGPT (OpenAI) streaming

    master

    To use the OpenAI API for streaming completions, use a POST method, set the Content-Type to application/json, and include stream: true in the body. Set pollingInterval: 0 to prevent the client from attempting to reconnect after the stream finishes.

    import { useEffect, useState } from "react";
    import { Text, View } from "react-native";
    import EventSource from "react-native-sse";
    
    const OpenAIToken = '[Your OpenAI token]';
    
    export default function App() {
      const [text, setText] = useState<string>("Loading...");
    
      useEffect(() => {
        const es = new EventSource(
          "https://api.openai.com/v1/chat/completions",
          {
            headers: {
              "Content-Type": "application/json",
              Authorization: `Bearer ${OpenAIToken}`,
            },
            method: "POST",
            body: JSON.stringify({
              model: "gpt-3.5-turbo-0125",
              messages: [{ role: "user", content: "What is the meaning of life?" }],
              stream: true,
            }),
            pollingInterval: 0, // Disable reconnections
          }
        );
    
        es.addEventListener("message", (event) => {
          if (event.data !== "[DONE]") {
            const data = JSON.parse(event.data);
            if (data.choices[0].delta.content !== undefined) {
              setText((prev) => prev + data.choices[0].delta.content);
            }
          }
        });
    
        return () => es.close();
      }, []);
    
      return (
        <View><Text>{text}</Text></View>
      );
    }
  7. Connect to an SSE server and listen for events

    master

    To use the library, import EventSource and instantiate it with a URL. You can then attach listeners for standard events like open, message, error, done, and close using addEventListener.

    import EventSource from "react-native-sse";
    
    const es = new EventSource("https://your-sse-server.com/.well-known/mercure");
    
    es.addEventListener("open", (event) => {
      console.log("Open SSE connection.");
    });
    
    es.addEventListener("message", (event) => {
      console.log("New message event:", event.data);
    });
    
    es.addEventListener("error", (event) => {
      if (event.type === "error") {
        console.error("Connection error:", event.message);
      } else if (event.type === "exception") {
        console.error("Error:", event.message, event.error);
      }
    });
    
    es.addEventListener("done", (event) => {
      console.log("Done SSE connection.");
    });
    
    es.addEventListener("close", (event) => {
      console.log("Close SSE connection.");
    });
  8. Handle custom server events with TypeScript

    master

    You can use TypeScript generics to provide type safety for custom event names sent by the server. This allows you to use EventSourceListener or EventSourceEvent with specific event types.

    import EventSource, { EventSourceListener, EventSourceEvent } from "react-native-sse";
    
    type MyCustomEvents = "ping" | "clientConnected" | "clientDisconnected";
    
    // 1. Using generics on the constructor
    const es = new EventSource<MyCustomEvents>("https://your-sse-server.com/.well-known/hub");
    
    // 2. Using a typed listener for all events
    const listener: EventSourceListener<MyCustomEvents> = (event) => {
      if (event.type === 'ping') {
        // ...
      }
    };
    
    // 3. Using a generic type for a specific event
    const pingListener: EventSourceListener<MyCustomEvents, 'ping'> = (event) => {
      console.log(event.data);
    };
    
    // 4. Or using EventSourceEvent directly
    const pingListenerAlt = (event: EventSourceEvent<'ping', MyCustomEvents>) => {
      // ...
    };
    
    es.addEventListener('ping', pingListener);
  9. Configure EventSource options

    master

    When instantiating EventSource, you can pass an options object to customize the connection behavior.

    OptionTypeDefaultDescription
    methodstring'GET'The HTTP method to use for the request.
    timeoutnumber0Timeout in milliseconds for the request.
    timeoutBeforeConnectionnumber500Delay in ms before attempting the first connection.
    withCredentialsbooleanfalseWhether to include credentials in the request.
    bodyanyundefinedThe request body (useful for POST requests).
    debugbooleanfalseIf true, enables debug logging to console.debug.
    intervalnumber5000Polling interval in ms used when the connection is DONE or an error occurs.
    lineEndingCharacterstringnullExplicitly specify the character used for line endings (e.g., \n). If null, the library attempts to auto-detect.
    headersobject{}Custom HTTP headers. These are merged with default headers (Accept: text/event-stream, Cache-Control: no-cache, X-Requested-With: XMLHttpRequest).
    const options = {
      method: 'POST',
      body: JSON.stringify({ foo: 'bar' }),
      headers: {
        'Content-Type': 'application/json',
        'Custom-Header': 'value'
      },
      timeout: 10000,
      debug: true
    };
    
    const es = new EventSource('https://example.com/stream', options);
  10. Configure EventSource options

    master

    The EventSource constructor accepts an optional EventSourceOptions object to customize the connection behavior.

    const options: EventSourceOptions = {
      method: 'GET', // Request method. Default: GET
      timeout: 0, // Time (ms) after which the connection will expire without any activity. Default: 0 (no timeout)
      timeoutBeforeConnection: 500, // Time (ms) to wait before initial connection is made. Default: 500
      withCredentials: false, // Include credentials in cross-site Access-Control requests. Default: false
      headers: {}, // Your request headers. Default: {}
      body: undefined, // Your request body sent on connection. Default: undefined
      debug: false, // Show console.debug messages for debugging purpose. Default: false
      pollingInterval: 5000, // Time (ms) between reconnections. If set to 0, reconnections will be disabled. Default: 5000
      lineEndingCharacter: null // Character(s) used to represent line endings in received data. Default: null
    }
  11. Manage EventSource event listeners

    master

    You can manage event subscriptions using the following methods:

    • addEventListener(type, listener): Registers a callback for a specific event type. Supported types include open, message, error, done, close, and any custom event type sent by the server via the event: field.
    • removeEventListener(type, listener): Removes a specific listener from an event type.
    • removeAllEventListeners(type?): Removes all listeners for a specific type. If no type is provided, it clears all listeners for all event types.
    const handler = (e) => console.log(e);
    
    es.addEventListener('message', handler);
    
    // Remove specific handler
    es.removeEventListener('message', handler);
    
    // Remove all 'message' handlers
    es.removeAllEventListeners('message');
    
    // Remove everything
    es.removeAllEventListeners();
  12. Import the EventSource class

    master

    The react-native-sse package exports the EventSource class as its primary entry point. You can import it using standard CommonJS require or ES6 import syntax to establish Server-Sent Events (SSE) connections in your React Native application.

    const EventSource = require('react-native-sse');
    
    // Or using ES6 imports
    import EventSource from 'react-native-sse';