react-realtime-hooks 1.0.2 → 1.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +6 -1
- package/dist/index.cjs +112 -5
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +8 -2
- package/dist/index.d.ts +8 -2
- package/dist/index.js +112 -5
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -402,6 +402,7 @@ var useHeartbeat = (options) => {
|
|
|
402
402
|
const startOnMount = options.startOnMount ?? true;
|
|
403
403
|
const intervalRef = useRef(createManagedInterval());
|
|
404
404
|
const timeoutRef = useRef(createManagedTimeout());
|
|
405
|
+
const generationRef = useRef(0);
|
|
405
406
|
const [state, setState] = useState(
|
|
406
407
|
() => createInitialState2(enabled && startOnMount)
|
|
407
408
|
);
|
|
@@ -428,8 +429,7 @@ var useHeartbeat = (options) => {
|
|
|
428
429
|
handleTimeout();
|
|
429
430
|
}, options.timeoutMs);
|
|
430
431
|
});
|
|
431
|
-
const
|
|
432
|
-
const performedAt = Date.now();
|
|
432
|
+
const handleBeatSuccess = useEffectEvent((performedAt) => {
|
|
433
433
|
commitState((current) => ({
|
|
434
434
|
...current,
|
|
435
435
|
hasTimedOut: false,
|
|
@@ -437,7 +437,35 @@ var useHeartbeat = (options) => {
|
|
|
437
437
|
}));
|
|
438
438
|
scheduleTimeout();
|
|
439
439
|
options.onBeat?.();
|
|
440
|
-
|
|
440
|
+
});
|
|
441
|
+
const handleBeatError = useEffectEvent((error) => {
|
|
442
|
+
timeoutRef.current.cancel();
|
|
443
|
+
options.onError?.(error);
|
|
444
|
+
});
|
|
445
|
+
const runBeat = useEffectEvent(() => {
|
|
446
|
+
const generation = generationRef.current;
|
|
447
|
+
const completeBeat = (result) => {
|
|
448
|
+
if (generation !== generationRef.current || result === false) {
|
|
449
|
+
return;
|
|
450
|
+
}
|
|
451
|
+
handleBeatSuccess(Date.now());
|
|
452
|
+
};
|
|
453
|
+
const failBeat = (error) => {
|
|
454
|
+
if (generation !== generationRef.current) {
|
|
455
|
+
return;
|
|
456
|
+
}
|
|
457
|
+
handleBeatError(error);
|
|
458
|
+
};
|
|
459
|
+
try {
|
|
460
|
+
const result = options.beat?.();
|
|
461
|
+
if (result !== null && typeof result === "object" && "then" in result) {
|
|
462
|
+
void Promise.resolve(result).then(completeBeat, failBeat);
|
|
463
|
+
return;
|
|
464
|
+
}
|
|
465
|
+
completeBeat(result);
|
|
466
|
+
} catch (error) {
|
|
467
|
+
failBeat(error);
|
|
468
|
+
}
|
|
441
469
|
});
|
|
442
470
|
const start = () => {
|
|
443
471
|
if (!enabled) {
|
|
@@ -455,6 +483,7 @@ var useHeartbeat = (options) => {
|
|
|
455
483
|
}));
|
|
456
484
|
};
|
|
457
485
|
const stop = () => {
|
|
486
|
+
generationRef.current += 1;
|
|
458
487
|
intervalRef.current.cancel();
|
|
459
488
|
timeoutRef.current.cancel();
|
|
460
489
|
commitState((current) => ({
|
|
@@ -500,6 +529,7 @@ var useHeartbeat = (options) => {
|
|
|
500
529
|
return void 0;
|
|
501
530
|
}, [enabled, options.intervalMs, startOnMount]);
|
|
502
531
|
useEffect(() => () => {
|
|
532
|
+
generationRef.current += 1;
|
|
503
533
|
intervalRef.current.cancel();
|
|
504
534
|
timeoutRef.current.cancel();
|
|
505
535
|
}, []);
|
|
@@ -605,6 +635,7 @@ var toProtocolsDependency = (protocols) => {
|
|
|
605
635
|
return Array.isArray(protocols) ? protocols.join("|") : protocols;
|
|
606
636
|
};
|
|
607
637
|
var toHeartbeatConfig = (heartbeat) => heartbeat === void 0 || heartbeat === false ? null : heartbeat;
|
|
638
|
+
var isSocketActive = (socket) => socket.readyState === WebSocket.OPEN || socket.readyState === WebSocket.CONNECTING;
|
|
608
639
|
var useWebSocket = (options) => {
|
|
609
640
|
const connect = options.connect ?? true;
|
|
610
641
|
const supported = isWebSocketSupported();
|
|
@@ -616,6 +647,7 @@ var useWebSocket = (options) => {
|
|
|
616
647
|
const manualOpenRef = useRef(false);
|
|
617
648
|
const skipCloseReconnectRef = useRef(false);
|
|
618
649
|
const suppressReconnectRef = useRef(false);
|
|
650
|
+
const pendingCloseActionRef = useRef(null);
|
|
619
651
|
const terminalErrorRef = useRef(null);
|
|
620
652
|
const [openNonce, setOpenNonce] = useState(0);
|
|
621
653
|
const [state, setState] = useState(
|
|
@@ -634,6 +666,7 @@ var useWebSocket = (options) => {
|
|
|
634
666
|
const heartbeatConfig = toHeartbeatConfig(
|
|
635
667
|
options.heartbeat
|
|
636
668
|
);
|
|
669
|
+
const defaultHeartbeatAction = options.reconnect === false ? "close" : "reconnect";
|
|
637
670
|
const heartbeatHookOptions = heartbeatConfig === null ? {
|
|
638
671
|
enabled: false,
|
|
639
672
|
intervalMs: 1e3,
|
|
@@ -669,6 +702,27 @@ var useWebSocket = (options) => {
|
|
|
669
702
|
if (heartbeatConfig !== null && heartbeatConfig.onTimeout !== void 0) {
|
|
670
703
|
heartbeatHookOptions.onTimeout = heartbeatConfig.onTimeout;
|
|
671
704
|
}
|
|
705
|
+
if (heartbeatConfig !== null) {
|
|
706
|
+
const onTimeout = heartbeatHookOptions.onTimeout;
|
|
707
|
+
heartbeatHookOptions.onTimeout = () => {
|
|
708
|
+
applyHeartbeatAction(
|
|
709
|
+
heartbeatConfig.timeoutAction ?? defaultHeartbeatAction,
|
|
710
|
+
new Event("heartbeat-timeout"),
|
|
711
|
+
"heartbeat-timeout"
|
|
712
|
+
);
|
|
713
|
+
onTimeout?.();
|
|
714
|
+
};
|
|
715
|
+
const onError = heartbeatConfig.onError;
|
|
716
|
+
heartbeatHookOptions.onError = (error) => {
|
|
717
|
+
const event = error instanceof Event ? error : new Event("heartbeat-error");
|
|
718
|
+
applyHeartbeatAction(
|
|
719
|
+
heartbeatConfig.errorAction ?? heartbeatConfig.timeoutAction ?? defaultHeartbeatAction,
|
|
720
|
+
event,
|
|
721
|
+
"error"
|
|
722
|
+
);
|
|
723
|
+
onError?.(error);
|
|
724
|
+
};
|
|
725
|
+
}
|
|
672
726
|
const heartbeat = useHeartbeat(
|
|
673
727
|
heartbeatHookOptions
|
|
674
728
|
);
|
|
@@ -684,10 +738,46 @@ var useWebSocket = (options) => {
|
|
|
684
738
|
}
|
|
685
739
|
socketRef.current = null;
|
|
686
740
|
socketKeyRef.current = null;
|
|
687
|
-
if (socket2
|
|
741
|
+
if (isSocketActive(socket2)) {
|
|
688
742
|
socket2.close(code, reason);
|
|
689
743
|
}
|
|
690
744
|
});
|
|
745
|
+
const applyHeartbeatAction = useEffectEvent(
|
|
746
|
+
(action, error, reconnectTrigger) => {
|
|
747
|
+
heartbeat.stop();
|
|
748
|
+
if (action === "none") {
|
|
749
|
+
commitState((current) => ({
|
|
750
|
+
...current,
|
|
751
|
+
lastChangedAt: Date.now(),
|
|
752
|
+
lastError: error
|
|
753
|
+
}));
|
|
754
|
+
return;
|
|
755
|
+
}
|
|
756
|
+
const shouldReconnect = action === "reconnect" && reconnectEnabled && (options.shouldReconnect?.(error) ?? true);
|
|
757
|
+
manualOpenRef.current = false;
|
|
758
|
+
terminalErrorRef.current = shouldReconnect ? null : error;
|
|
759
|
+
const socket2 = socketRef.current;
|
|
760
|
+
if (socket2 === null || !isSocketActive(socket2)) {
|
|
761
|
+
commitState((current) => ({
|
|
762
|
+
...current,
|
|
763
|
+
lastChangedAt: Date.now(),
|
|
764
|
+
lastError: error,
|
|
765
|
+
status: shouldReconnect ? "reconnecting" : "error"
|
|
766
|
+
}));
|
|
767
|
+
if (shouldReconnect) {
|
|
768
|
+
reconnect.schedule(reconnectTrigger);
|
|
769
|
+
}
|
|
770
|
+
return;
|
|
771
|
+
}
|
|
772
|
+
pendingCloseActionRef.current = {
|
|
773
|
+
error,
|
|
774
|
+
reconnectTrigger: shouldReconnect ? reconnectTrigger : null
|
|
775
|
+
};
|
|
776
|
+
skipCloseReconnectRef.current = true;
|
|
777
|
+
suppressReconnectRef.current = true;
|
|
778
|
+
closeSocket();
|
|
779
|
+
}
|
|
780
|
+
);
|
|
691
781
|
const parseMessage = useEffectEvent((event) => {
|
|
692
782
|
const parser = options.parseMessage ?? defaultParseMessage;
|
|
693
783
|
return parser(event);
|
|
@@ -755,9 +845,26 @@ var useWebSocket = (options) => {
|
|
|
755
845
|
socketKeyRef.current = null;
|
|
756
846
|
heartbeat.stop();
|
|
757
847
|
updateBufferedAmount();
|
|
848
|
+
const pendingCloseAction = pendingCloseActionRef.current;
|
|
849
|
+
pendingCloseActionRef.current = null;
|
|
758
850
|
const terminalError = terminalErrorRef.current;
|
|
759
851
|
const skipCloseReconnect = skipCloseReconnectRef.current;
|
|
760
852
|
skipCloseReconnectRef.current = false;
|
|
853
|
+
if (pendingCloseAction !== null) {
|
|
854
|
+
suppressReconnectRef.current = false;
|
|
855
|
+
commitState((current) => ({
|
|
856
|
+
...current,
|
|
857
|
+
lastChangedAt: Date.now(),
|
|
858
|
+
lastCloseEvent: event,
|
|
859
|
+
lastError: pendingCloseAction.error ?? current.lastError,
|
|
860
|
+
status: pendingCloseAction.reconnectTrigger === null ? "error" : "reconnecting"
|
|
861
|
+
}));
|
|
862
|
+
options.onClose?.(event);
|
|
863
|
+
if (pendingCloseAction.reconnectTrigger !== null) {
|
|
864
|
+
reconnect.schedule(pendingCloseAction.reconnectTrigger);
|
|
865
|
+
}
|
|
866
|
+
return;
|
|
867
|
+
}
|
|
761
868
|
if (terminalError !== null) {
|
|
762
869
|
suppressReconnectRef.current = false;
|
|
763
870
|
commitState((current) => ({
|
|
@@ -916,7 +1023,7 @@ var useWebSocket = (options) => {
|
|
|
916
1023
|
if (socket2 === null) {
|
|
917
1024
|
return;
|
|
918
1025
|
}
|
|
919
|
-
if (socket2
|
|
1026
|
+
if (isSocketActive(socket2)) {
|
|
920
1027
|
socket2.close();
|
|
921
1028
|
}
|
|
922
1029
|
}, []);
|