react-native-sse icon indicating copy to clipboard operation
react-native-sse copied to clipboard

Add autoConnect option and isConnected getter to EventSource

Open feggaa opened this issue 8 months ago • 3 comments

feggaa avatar Jul 19 '25 11:07 feggaa

Why would you need an autoConnect option? If you want to disable the automatic reconnect, you can just set the pollingInterval to 0.

And instead of adding a new isConnected function, it would be both easier and more useful to expose the status property of the EventSource class.

EmilJunker avatar Jul 19 '25 12:07 EmilJunker

Thank you for your question

I need it for create instance of eventSource without connect

then I control connection by call eventSource.open() or eventSource.close()

this small example how I use it

type tEvent = {
    type: 'data' | 'error' | 'close' | 'remove';
    data: string | null;
    eventId : string;
}

const eventSource = new EventSource(global.serverUrl + 'serverEvents',{autoConnect: false});

function subscribeToEvent(api : string,args: string,eventId : string) {
    return fetch(global.serverUrl + 'serverEvents', {
        method: 'POST',
        headers: {
            'Content-Type': 'application/json',
        },
        body: JSON.stringify({api,args,eventId}),
    })
}

const listeners = new Map<string, (data :any)=> any>();

export function startListening<T>(api : string,callback : (data : T)=>void,args : any){
    if(!eventSource.isConnected){
        eventSource.open();
    }
    const eventHash = crc32.str(api +':'+ JSON.stringify(args)).toString()
    const randomId = Math.random().toString(36).substring(2, 15);
    const eventId = eventHash + ':' + randomId;
    subscribeToEvent(api,args,eventId)
    listeners.set(eventId,callback);

    return () => {
        listeners.delete(eventId);
        if (listeners.size === 0) {
            eventSource.close();
        }
    }
}

eventSource.addEventListener('message', (event) => {
    const data : tEvent | null = event.data ? JSON.parse(event.data) : null;
    if(!data) return
    const {type, data: eventData, eventId} = data;

    if (type === 'data') {
        const callback = listeners.get(eventId);
        const cache = storage.getString(eventId);
        if (cache) {
            callback?.(JSON.parse(cache));
        }
        callback?.(eventData);
    } else if (type === 'error') {
        console.error('Error event:', eventData);
    } else if (type === 'close') {
        eventSource.close();
        console.log('Connection closed');
    } else if (type === 'remove') {
        listeners.delete(eventId);
        console.log('Listener removed for eventId:', eventId);
    }
})
eventSource.addEventListener('error', (event) => {
    console.error('Error event:', event);
});

AppState.addEventListener('change', (state) => {
    if (state === 'active') {
        eventSource.open();
    } else {
        eventSource.close();
    }
})

export function useEventSource({api, args} : {api : string, args : any}) {
    const [_args, setArgs] = useState(args);
    const [data, setData] = useState<any>(null);
    const [eventId, setEventId] = useState<string | null>(null);
    
    useEffect(() => {
        const eventHash = crc32.str(api +':'+ JSON.stringify(args)).toString()
        const randomId = Math.random().toString(36).substring(2, 15);
        const eventId = eventHash + ':' + randomId;
        subscribeToEvent(api,args,eventId)
        listeners.set(eventId,setData);
        setEventId(eventId);
        return () => {
            listeners.delete(eventId);
            if (listeners.size === 0) {
                eventSource.close();
            }
        }
    },[_args])

    return {
        data,
        eventId,
        setArgs,
        remove : () => {
            if (!eventId) return;
            listeners.delete(eventId);
            if (listeners.size === 0) {
                eventSource.close();
            }
        },
    }
}

feggaa avatar Sep 02 '25 06:09 feggaa

This new option looks useful. I have the same issue — I also need to create the instance and open the connection only when needed.

SteeBono avatar Oct 18 '25 16:10 SteeBono