diff --git a/lib/tunnelbroker/tunnelbroker-context.js b/lib/tunnelbroker/tunnelbroker-context.js --- a/lib/tunnelbroker/tunnelbroker-context.js +++ b/lib/tunnelbroker/tunnelbroker-context.js @@ -67,6 +67,11 @@ +retryCount: number, }; +const defaultSocketState: TunnelbrokerSocketState = { + connected: false, + retryCount: 0, +}; + type TunnelbrokerContextType = { +sendMessageToDevice: ( message: TunnelbrokerClientMessageToDevice, @@ -152,9 +157,16 @@ const previousInitMessage = React.useRef(null); - const [socketState, setSocketState] = React.useState( - { connected: false, retryCount: 0 }, - ); + const [socketState, setSocketState] = + React.useState(defaultSocketState); + const socketStateRef = + React.useRef(defaultSocketState); + + const resetSocketState = React.useCallback(() => { + socketStateRef.current = defaultSocketState; + setSocketState(defaultSocketState); + }, []); + const listeners = React.useRef>(new Set()); const socket = React.useRef(null); const socketSessionCounter = React.useRef(0); @@ -176,9 +188,9 @@ stopHeartbeatTimeout(); heartbeatTimeoutID.current = setTimeout(() => { socket.current?.close(); - setSocketState({ connected: false, retryCount: 0 }); + resetSocketState(); }, tunnelbrokerHeartbeatTimeout); - }, [stopHeartbeatTimeout]); + }, [resetSocketState, stopHeartbeatTimeout]); // determine if the socket is active (not closed or closing) const isSocketActive = @@ -231,10 +243,14 @@ tunnelbrokerSocket.onclose = () => { // this triggers the effect hook again and reconnect - setSocketState(prev => ({ - connected: false, - retryCount: prev.connected ? 0 : prev.retryCount, - })); + setSocketState(prev => { + const newValue = { + connected: false, + retryCount: prev.connected ? 0 : prev.retryCount, + }; + socketStateRef.current = newValue; + return newValue; + }); onClose?.(); socket.current = null; console.log('Connection to Tunnelbroker closed'); @@ -244,10 +260,14 @@ 'Retrying Tunnelbroker connection. Attempt:', socketState.retryCount + 1, ); - setSocketState(prev => ({ - connected: false, - retryCount: prev.connected ? 0 : prev.retryCount + 1, - })); + setSocketState(prev => { + const newValue = { + connected: false, + retryCount: prev.connected ? 0 : prev.retryCount + 1, + }; + socketStateRef.current = newValue; + return newValue; + }); } }, clientTunnelbrokerSocketReconnectDelay); }; @@ -285,6 +305,7 @@ tunnelbrokerToDeviceMessageTypes.CONNECTION_INITIALIZATION_RESPONSE ) { if (message.status.type === 'Success' && !socketState.connected) { + socketStateRef.current = { connected: true, isAuthorized }; setSocketState({ connected: true, isAuthorized }); console.log( 'session with Tunnelbroker created. isAuthorized:', @@ -302,7 +323,7 @@ void invalidTokenLogOut(); return; } - setSocketState({ connected: false, retryCount: 0 }); + resetSocketState(); console.log( 'creating session with Tunnelbroker error:', message.status.data, @@ -352,13 +373,15 @@ socketState.connected, socketState.retryCount, invalidTokenLogOut, + resetSocketState, ]); const sendMessage: (request: DeviceToTunnelbrokerRequest) => Promise = React.useCallback( request => { const resultPromise: Promise = new Promise((resolve, reject) => { - const socketActive = socketState.connected && socket.current; + const socketActive = + socketStateRef.current.connected && socket.current; if (!shouldBeClosed && !socketActive) { throw new Error('Tunnelbroker not connected'); } @@ -380,7 +403,7 @@ return Promise.race([resultPromise, timeoutPromise]); }, - [socketState, secondaryTunnelbrokerConnection, shouldBeClosed], + [secondaryTunnelbrokerConnection, shouldBeClosed], ); const sendMessageToDevice: (