@@ -529,6 +529,14 @@ export const connect = async (
529529 navigate ,
530530 params ,
531531) => {
532+ // Socket already allocated, just reconnect it if needed.
533+ if ( socket . current ) {
534+ if ( ! socket . current . connected ) {
535+ socket . current . reconnect ( ) ;
536+ }
537+ return ;
538+ }
539+
532540 // Get backend URL object from the endpoint.
533541 const endpoint = getBackendURL ( EVENTURL ) ;
534542 const on_hydrated_queue = [ ] ;
@@ -540,7 +548,9 @@ export const connect = async (
540548 protocols : [ reflexEnvironment . version ] ,
541549 autoUnref : false ,
542550 query : { token : getToken ( ) } ,
551+ reconnection : false , // Reconnection will be handled manually.
543552 } ) ;
553+ socket . current . wait_connect = ! socket . current . connected ;
544554 // Ensure undefined fields in events are sent as null instead of removed
545555 socket . current . io . encoder . replacer = ( k , v ) => ( v === undefined ? null : v ) ;
546556 socket . current . io . decoder . tryParse = ( str ) => {
@@ -550,6 +560,18 @@ export const connect = async (
550560 return false ;
551561 }
552562 } ;
563+ // Set up a reconnect helper function
564+ socket . current . reconnect = ( ) => {
565+ if (
566+ socket . current &&
567+ ! socket . current . connected &&
568+ ! socket . current . wait_connect
569+ ) {
570+ socket . current . wait_connect = true ;
571+ socket . current . io . opts . query = { token : getToken ( ) } ; // Update token for reconnect.
572+ socket . current . connect ( ) ;
573+ }
574+ } ;
553575
554576 function checkVisibility ( ) {
555577 if ( document . visibilityState === "visible" ) {
@@ -565,7 +587,7 @@ export const connect = async (
565587 ) ;
566588 } else if ( ! socket . current . connected ) {
567589 console . log ( "Socket is disconnected, attempting to reconnect " ) ;
568- socket . current . connect ( ) ;
590+ socket . current . reconnect ( ) ;
569591 } else {
570592 console . log ( "Socket is reconnected " ) ;
571593 }
@@ -588,6 +610,7 @@ export const connect = async (
588610
589611 // Once the socket is open, hydrate the page.
590612 socket . current . on ( "connect" , async ( ) => {
613+ socket . current . wait_connect = false ;
591614 setConnectErrors ( [ ] ) ;
592615 window . addEventListener ( "pagehide" , pagehideHandler ) ;
593616 window . addEventListener ( "beforeunload" , disconnectTrigger ) ;
@@ -599,16 +622,33 @@ export const connect = async (
599622 } ) ;
600623
601624 socket . current . on ( "connect_error" , ( error ) => {
602- setConnectErrors ( ( connectErrors ) => [ connectErrors . slice ( - 9 ) , error ] ) ;
625+ socket . current . wait_connect = false ;
626+ let n_connect_errors = 0 ;
627+ setConnectErrors ( ( connectErrors ) => {
628+ const new_errors = [ ...connectErrors . slice ( - 9 ) , error ] ;
629+ n_connect_errors = new_errors . length ;
630+ return new_errors ;
631+ } ) ;
632+ window . setTimeout ( ( ) => {
633+ if ( socket . current && ! socket . current . connected ) {
634+ socket . current . reconnect ( ) ;
635+ }
636+ } , 200 * n_connect_errors ) ; // Incremental backoff
603637 } ) ;
604638
605639 // When the socket disconnects reset the event_processing flag
606- socket . current . on ( "disconnect" , ( ) => {
607- socket . current = null ; // allow reconnect to occur automatically
640+ socket . current . on ( "disconnect" , ( reason , details ) => {
641+ socket . current . wait_connect = false ;
642+ const try_reconnect =
643+ reason !== "io server disconnect" && reason !== "io client disconnect" ;
608644 event_processing = false ;
609645 window . removeEventListener ( "unload" , disconnectTrigger ) ;
610646 window . removeEventListener ( "beforeunload" , disconnectTrigger ) ;
611647 window . removeEventListener ( "pagehide" , pagehideHandler ) ;
648+ if ( try_reconnect ) {
649+ // Attempt to reconnect transient non-intentional disconnects.
650+ socket . current . reconnect ( ) ;
651+ }
612652 } ) ;
613653
614654 // On each received message, queue the updates and events.
@@ -785,6 +825,7 @@ export const useEventLoop = (
785825 const [ searchParams ] = useSearchParams ( ) ;
786826 const [ connectErrors , setConnectErrors ] = useState ( [ ] ) ;
787827 const params = useRef ( paramsR ) ;
828+ const mounted = useRef ( false ) ;
788829
789830 useEffect ( ( ) => {
790831 const { "*" : splat , ...remainingParams } = paramsR ;
@@ -796,11 +837,16 @@ export const useEventLoop = (
796837 } , [ paramsR ] ) ;
797838
798839 const ensureSocketConnected = useCallback ( async ( ) => {
840+ if ( ! mounted . current ) {
841+ // During hot reload, some components may still have a reference to
842+ // addEvents, so avoid reconnecting the socket of an unmounted event loop.
843+ return ;
844+ }
799845 // only use websockets if state is present and backend is not disabled (reflex cloud).
800846 if (
801847 Object . keys ( initialState ) . length > 1 &&
802848 ! isBackendDisabled ( ) &&
803- ! socket . current
849+ ! socket . current ?. connected
804850 ) {
805851 // Initialize the websocket connection.
806852 await connect (
@@ -813,13 +859,23 @@ export const useEventLoop = (
813859 ( ) => params . current ,
814860 ) ;
815861 }
816- } , [ socket , dispatch , setConnectErrors , client_storage , navigate , params ] ) ;
862+ } , [
863+ socket ,
864+ dispatch ,
865+ setConnectErrors ,
866+ client_storage ,
867+ navigate ,
868+ params ,
869+ mounted ,
870+ ] ) ;
817871
818872 // Function to add new events to the event queue.
819873 const addEvents = useCallback ( ( events , args , event_actions ) => {
820874 const _events = events . filter ( ( e ) => e !== undefined && e !== null ) ;
821-
822- ensureSocketConnected ( ) ;
875+ if ( ! event_actions ?. temporal ) {
876+ // Reconnect socket if needed for non-temporal events.
877+ ensureSocketConnected ( ) ;
878+ }
823879
824880 if ( ! ( args instanceof Array ) ) {
825881 args = [ args ] ;
@@ -914,12 +970,16 @@ export const useEventLoop = (
914970 // Handle socket connect/disconnect.
915971 useEffect ( ( ) => {
916972 // Initialize the websocket connection.
973+ mounted . current = true ;
917974 ensureSocketConnected ( ) ;
918975
919976 // Cleanup function.
920977 return ( ) => {
978+ mounted . current = false ;
921979 if ( socket . current ) {
922980 socket . current . disconnect ( ) ;
981+ socket . current . off ( ) ;
982+ socket . current = null ;
923983 }
924984 } ;
925985 } , [ ] ) ;
0 commit comments