react-realtime-hooks 1.2.0 → 1.2.1

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/dist/index.cjs CHANGED
@@ -826,6 +826,9 @@ var useWebSocket = (options) => {
826
826
  const protocolsDependency = toProtocolsDependency(options.protocols);
827
827
  const socketRef = react.useRef(null);
828
828
  const socketKeyRef = react.useRef(null);
829
+ const activeSocketEpochRef = react.useRef(null);
830
+ const closingSocketEpochRef = react.useRef(null);
831
+ const nextSocketEpochRef = react.useRef(0);
829
832
  const manualCloseRef = react.useRef(false);
830
833
  const manualOpenRef = react.useRef(false);
831
834
  const skipCloseReconnectRef = react.useRef(false);
@@ -914,17 +917,28 @@ var useWebSocket = (options) => {
914
917
  stateRef.current = resolved;
915
918
  setState(resolved);
916
919
  };
917
- const closeSocket = react.useEffectEvent((code, reason) => {
918
- const socket2 = socketRef.current;
919
- if (socket2 === null) {
920
- return;
921
- }
922
- socketRef.current = null;
923
- socketKeyRef.current = null;
924
- if (isSocketActive(socket2)) {
925
- socket2.close(code, reason);
926
- }
920
+ const isActiveSocketEvent = react.useEffectEvent((socketEpoch) => {
921
+ return activeSocketEpochRef.current === socketEpoch;
927
922
  });
923
+ const shouldHandleSocketClose = react.useEffectEvent((socketEpoch) => {
924
+ return activeSocketEpochRef.current === socketEpoch || closingSocketEpochRef.current === socketEpoch;
925
+ });
926
+ const closeSocket = react.useEffectEvent(
927
+ (config = {}) => {
928
+ const socket2 = socketRef.current;
929
+ const socketEpoch = activeSocketEpochRef.current;
930
+ if (socket2 === null || socketEpoch === null) {
931
+ return;
932
+ }
933
+ socketRef.current = null;
934
+ socketKeyRef.current = null;
935
+ activeSocketEpochRef.current = null;
936
+ closingSocketEpochRef.current = config.trackClose ? socketEpoch : null;
937
+ if (isSocketActive(socket2)) {
938
+ socket2.close(config.code, config.reason);
939
+ }
940
+ }
941
+ );
928
942
  const applyHeartbeatAction = react.useEffectEvent(
929
943
  (action, error, reconnectTrigger) => {
930
944
  heartbeat.stop();
@@ -959,7 +973,7 @@ var useWebSocket = (options) => {
959
973
  };
960
974
  skipCloseReconnectRef.current = true;
961
975
  suppressReconnectRef.current = true;
962
- closeSocket();
976
+ closeSocket({ trackClose: true });
963
977
  }
964
978
  );
965
979
  const parseMessage = react.useEffectEvent((event) => {
@@ -1013,10 +1027,17 @@ var useWebSocket = (options) => {
1013
1027
  lastError: parseError,
1014
1028
  status: "error"
1015
1029
  }));
1016
- closeSocket(1003, "parse-error");
1030
+ closeSocket({
1031
+ code: 1003,
1032
+ reason: "parse-error",
1033
+ trackClose: true
1034
+ });
1017
1035
  }
1018
1036
  });
1019
- const handleError = react.useEffectEvent((event) => {
1037
+ const handleError = react.useEffectEvent((event, socketEpoch) => {
1038
+ if (!isActiveSocketEvent(socketEpoch)) {
1039
+ return;
1040
+ }
1020
1041
  heartbeat.stop();
1021
1042
  commitState((current) => ({
1022
1043
  ...current,
@@ -1026,9 +1047,18 @@ var useWebSocket = (options) => {
1026
1047
  }));
1027
1048
  options.onError?.(event);
1028
1049
  });
1029
- const handleClose = react.useEffectEvent((event) => {
1030
- socketRef.current = null;
1031
- socketKeyRef.current = null;
1050
+ const handleClose = react.useEffectEvent((event, socketEpoch) => {
1051
+ if (!shouldHandleSocketClose(socketEpoch)) {
1052
+ return;
1053
+ }
1054
+ if (activeSocketEpochRef.current === socketEpoch) {
1055
+ socketRef.current = null;
1056
+ socketKeyRef.current = null;
1057
+ activeSocketEpochRef.current = null;
1058
+ }
1059
+ if (closingSocketEpochRef.current === socketEpoch) {
1060
+ closingSocketEpochRef.current = null;
1061
+ }
1032
1062
  heartbeat.stop();
1033
1063
  updateBufferedAmount();
1034
1064
  const pendingCloseAction = pendingCloseActionRef.current;
@@ -1108,7 +1138,11 @@ var useWebSocket = (options) => {
1108
1138
  lastChangedAt: Date.now(),
1109
1139
  status: "closing"
1110
1140
  }));
1111
- closeSocket(code, reason);
1141
+ closeSocket({
1142
+ code,
1143
+ reason,
1144
+ trackClose: true
1145
+ });
1112
1146
  };
1113
1147
  const send = (message) => {
1114
1148
  const socket2 = socketRef.current;
@@ -1160,8 +1194,12 @@ var useWebSocket = (options) => {
1160
1194
  return;
1161
1195
  }
1162
1196
  const socket2 = new WebSocket(resolvedUrl, options.protocols);
1197
+ const socketEpoch = nextSocketEpochRef.current + 1;
1163
1198
  socketRef.current = socket2;
1164
1199
  socketKeyRef.current = nextSocketKey;
1200
+ activeSocketEpochRef.current = socketEpoch;
1201
+ closingSocketEpochRef.current = null;
1202
+ nextSocketEpochRef.current = socketEpoch;
1165
1203
  socket2.binaryType = options.binaryType ?? "blob";
1166
1204
  commitState((current) => ({
1167
1205
  ...current,
@@ -1170,16 +1208,22 @@ var useWebSocket = (options) => {
1170
1208
  status: reconnect.status === "running" || reconnect.status === "scheduled" ? "reconnecting" : "connecting"
1171
1209
  }));
1172
1210
  const handleSocketOpen = (event) => {
1211
+ if (!isActiveSocketEvent(socketEpoch)) {
1212
+ return;
1213
+ }
1173
1214
  handleOpen(event, socket2);
1174
1215
  };
1175
1216
  const handleSocketMessage = (event) => {
1217
+ if (!isActiveSocketEvent(socketEpoch)) {
1218
+ return;
1219
+ }
1176
1220
  handleMessage(event);
1177
1221
  };
1178
1222
  const handleSocketError = (event) => {
1179
- handleError(event);
1223
+ handleError(event, socketEpoch);
1180
1224
  };
1181
1225
  const handleSocketClose = (event) => {
1182
- handleClose(event);
1226
+ handleClose(event, socketEpoch);
1183
1227
  };
1184
1228
  socket2.addEventListener("open", handleSocketOpen);
1185
1229
  socket2.addEventListener("message", handleSocketMessage);
@@ -1203,6 +1247,8 @@ var useWebSocket = (options) => {
1203
1247
  react.useEffect(() => () => {
1204
1248
  suppressReconnectRef.current = true;
1205
1249
  socketKeyRef.current = null;
1250
+ activeSocketEpochRef.current = null;
1251
+ closingSocketEpochRef.current = null;
1206
1252
  terminalErrorRef.current = null;
1207
1253
  const socket2 = socketRef.current;
1208
1254
  socketRef.current = null;