react-mirrorstate 0.3.1 → 0.5.0
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/connection-manager.js +5 -2
- package/dist/index.js +38 -4
- package/package.json +1 -1
|
@@ -93,6 +93,10 @@ class WebSocketConnectionManager {
|
|
|
93
93
|
}
|
|
94
94
|
pendingUpdates = new Map();
|
|
95
95
|
updateState(name, state) {
|
|
96
|
+
// Immediately update currentStates so subsequent reads get the latest value
|
|
97
|
+
this.currentStates.set(name, state);
|
|
98
|
+
// Notify all local subscribers immediately (for same-page component sync)
|
|
99
|
+
this.notifyListeners(name, state);
|
|
96
100
|
if (this.ws?.readyState !== WebSocket.OPEN) {
|
|
97
101
|
this.queuedUpdates.set(name, state);
|
|
98
102
|
return;
|
|
@@ -102,7 +106,7 @@ class WebSocketConnectionManager {
|
|
|
102
106
|
if (pendingUpdate) {
|
|
103
107
|
clearTimeout(pendingUpdate);
|
|
104
108
|
}
|
|
105
|
-
// Debounce rapid
|
|
109
|
+
// Debounce rapid WebSocket sends
|
|
106
110
|
const timeout = setTimeout(() => {
|
|
107
111
|
if (!this.ws) {
|
|
108
112
|
return;
|
|
@@ -111,7 +115,6 @@ class WebSocketConnectionManager {
|
|
|
111
115
|
name,
|
|
112
116
|
state,
|
|
113
117
|
}));
|
|
114
|
-
this.currentStates.set(name, state);
|
|
115
118
|
this.pendingUpdates.delete(name);
|
|
116
119
|
}, 10);
|
|
117
120
|
this.pendingUpdates.set(name, timeout);
|
package/dist/index.js
CHANGED
|
@@ -2,6 +2,36 @@ import { useEffect, useState, useRef } from "react";
|
|
|
2
2
|
import { produce } from "immer";
|
|
3
3
|
import { connectionManager } from "./connection-manager";
|
|
4
4
|
import { INITIAL_STATES } from "virtual:mirrorstate/initial-states";
|
|
5
|
+
// Batching state for each mirror state name
|
|
6
|
+
const batchQueues = new Map();
|
|
7
|
+
const batchPending = new Map();
|
|
8
|
+
function scheduleBatchFlush(name) {
|
|
9
|
+
if (batchPending.get(name)) {
|
|
10
|
+
return;
|
|
11
|
+
}
|
|
12
|
+
batchPending.set(name, true);
|
|
13
|
+
queueMicrotask(() => {
|
|
14
|
+
const queue = batchQueues.get(name);
|
|
15
|
+
if (!queue || queue.length === 0) {
|
|
16
|
+
batchPending.set(name, false);
|
|
17
|
+
return;
|
|
18
|
+
}
|
|
19
|
+
// Apply all queued updates in sequence
|
|
20
|
+
// Get current state from connection manager, falling back to INITIAL_STATES
|
|
21
|
+
let currentState = connectionManager.getCurrentState(name) ??
|
|
22
|
+
INITIAL_STATES?.[name];
|
|
23
|
+
// Apply each update sequentially, handling both object mutations and primitive returns
|
|
24
|
+
let newState = currentState;
|
|
25
|
+
queue.forEach((updater) => {
|
|
26
|
+
newState = produce(newState, updater);
|
|
27
|
+
});
|
|
28
|
+
// Clear the queue
|
|
29
|
+
batchQueues.set(name, []);
|
|
30
|
+
batchPending.set(name, false);
|
|
31
|
+
// Update connection manager (which notifies all subscribers)
|
|
32
|
+
connectionManager.updateState(name, newState);
|
|
33
|
+
});
|
|
34
|
+
}
|
|
5
35
|
export function useMirrorState(name, initialValue) {
|
|
6
36
|
// Capture initialValue once on first render to make it stable
|
|
7
37
|
const initialValueRef = useRef(initialValue);
|
|
@@ -24,10 +54,14 @@ export function useMirrorState(name, initialValue) {
|
|
|
24
54
|
};
|
|
25
55
|
}, [name]);
|
|
26
56
|
const updateMirrorState = (updater) => {
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
57
|
+
// Initialize batch queue for this name if needed
|
|
58
|
+
if (!batchQueues.has(name)) {
|
|
59
|
+
batchQueues.set(name, []);
|
|
60
|
+
}
|
|
61
|
+
// Add updater to batch queue
|
|
62
|
+
batchQueues.get(name).push(updater);
|
|
63
|
+
// Schedule batch flush
|
|
64
|
+
scheduleBatchFlush(name);
|
|
31
65
|
};
|
|
32
66
|
return [state, updateMirrorState];
|
|
33
67
|
}
|