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.
@@ -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 updates
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
- const currentState = connectionManager.getCurrentState(name) ?? state;
28
- const newState = produce(currentState, updater);
29
- connectionManager.updateState(name, newState);
30
- setState(newState);
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
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-mirrorstate",
3
- "version": "0.3.1",
3
+ "version": "0.5.0",
4
4
  "description": "React library for bidirectional state synchronization with MirrorState",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",