binaryminds / react-native-sse

Event Source implementation for React Native. Server-Sent Events (SSE) for iOS and Android 🚀
https://www.npmjs.com/package/react-native-sse
MIT License
202 stars 30 forks source link

how do I reestablish a connection if my server restarts? #55

Open karimcambridge opened 4 months ago

karimcambridge commented 4 months ago

when my server restarts this doesn't seem to reestablish my connection, how do I detect that the connection was closed?

EmilJunker commented 4 months ago

This question has been asked before (see #37). I didn't understand it then and I don't understand it now. Could you be more specific about what you mean?

When the server closes the HTTP connection, react-native-sse will automatically reopen it. But I guess when the server just restarts abruptly, it won't try to reconnect. And I honestly don't see a reason why one would expect it to do that, but maybe I'm missing something?

karimcambridge commented 4 months ago

We need it to automatically reconnect, lets say we restart the servers and clients are online on their mobile device. react-native-sse is not automaticaly reconnecting.

Right now I added a useeffect dependency with my socket.connected from socket.io to trigger a reconnect and if status is closed. This is my hook example;

import { useEffect, useRef, useState } from 'react';
import EventSource from 'react-native-sse';

import { getServerApiUrl } from '../../api/config';

type EventSourceConstructor = {
    new(url: string, eventSourceInitDict?: EventSourceInit): EventSource
};

export type EventSourceStatus = 'init' | 'open' | 'closed' | 'error';

export type EventSourceEvent = Event & { data: string };

export const useEventSource = (user, serverConnection, withCredentials: boolean = false, ESClass: EventSourceConstructor = EventSource) => {
    const source = useRef<EventSource | null>(null);
    const [status, setStatus] = useState<EventSourceStatus>('init');

    useEffect(() => {
        if(user && status === 'closed') {
            const es = new ESClass(getServerApiUrl() + '/v1/sse/rider/' + user?.id, { withCredentials });
            source.current = es;

            es.addEventListener('open', () => {
                setStatus('open');
                console.log('[sse]: connected to ' + getServerApiUrl() + '/v1/sse/rider/' + user.id);
            });
            es.addEventListener('error', () => {
                setStatus('error');
                console.log('[sse; error]: connecting to ' + getServerApiUrl() + '/v1/sse/rider/' + user.id);
            });

            return () => {
                source.current = null;
                es.removeAllEventListeners();
                es.close();
            };
        }
        setStatus('closed');

        return undefined;
    }, [serverConnection, withCredentials, ESClass]);

    return [source.current, status] as const;
};

export function useEventSourceListener(
    source: EventSource | null,
    types: string[],
    listener: (e: EventSourceEvent) => void,
    dependencies: any[] = []
) {
    useEffect(() => {
        if(source) {
            types.forEach((type) => source.addEventListener(type, listener as any));
            return () => types.forEach((type) => source.removeEventListener(type, listener as any));
        }
        return undefined;
        // eslint-disable-next-line react-hooks/exhaustive-deps
    }, [source, ...dependencies]);
}
EmilJunker commented 4 months ago

You could simply add logic to automatically reconnect when no SSE event messages come in for x seconds, e.g. using setTimeout. That would usually imply that the connection is dead.

Maybe there would also be a way to handle this in the react-native-sse library itself, but I'm not sure about that.

karimcambridge commented 4 months ago

would probably need to implement a heartbeat, it can work but unnecessary load.

i thought sses could detect when they lose connection, like we should have the readyState exposed for us to read.

EmilJunker commented 4 months ago

i thought sses could detect when they lose connection, like we should have the readyState exposed for us to read.

We already do an automatic reconnect when the readyState switches to DONE. I think the problem is that the client doesn't event receive a new readyState when the server is abruptly shut down. Thus, there is no direct way for the client to know that the connection is gone. If you find one, please let us know.