Reference solution
The findings below are ordered by priority.
Findings
1. Every state update tears down and reconnects presence
Location: src/use-cursor-presence.tsx:19-32,59-62
Both localCursor and participants are effect dependencies, and the effect updates them from its listeners. Pointer movement or any remote message therefore closes the connection and installs a new one, resetting sequence state and changing generations during ordinary activity.
Keep the connection effect scoped to stable transport and identity inputs. Store the latest outbound coordinate and sequence in refs, and use functional state updates for rendered peers.
2. Pointer events schedule one uncancelled send each
Location: src/use-cursor-presence.tsx:45-49
Every pointer event creates its own 50 ms timeout with that event's coordinate. Rapid movement produces far more than 20 updates per second, sends stale intermediate positions, and leaves callbacks targeting a closed connection after navigation.
Maintain one cancellable throttle timer and a ref containing the latest coordinate; send once per interval and cancel the timer during cleanup.
3. Late, cross-document, duplicate, and self messages are all accepted
Location: src/use-cursor-presence.tsx:20-31
The callback never checks document ID, connection generation, local participant identity, or the last accepted sequence. Old connections can repopulate a newly opened document, duplicate or reordered packets can move a cursor backward, and the local echo is rendered as a peer.
Validate all four identity and ordering conditions before applying a message, retaining the greatest sequence per participant for the current generation.
4. Remote updates use a stale participant snapshot
Location: src/use-cursor-presence.tsx:21-30
The callback builds a new array from the participants captured when the effect ran. Two messages delivered before React reruns the effect can overwrite one another, and reconnect churn does not make that update atomic.
Use the functional state form and derive the replacement from the current array, after validating message sequence.
5. Expiry trusts a remote wall clock instead of local receipt time
Location: src/use-cursor-presence.tsx:25-29,54-57
receivedAt is populated from the sender's sentAt and compared with local Date.now. Clock skew can expire a live participant immediately or retain it indefinitely, and the injected monotonic clock is unused.
Stamp accepted messages with clock.now() locally and use the same monotonic source for the 15-second cutoff.
6. Cleanup leaves global listeners and intervals active
Location: src/use-cursor-presence.tsx:51-62
The cleanup closes only the connection. Each effect run leaves its pointer listener and interval installed, so navigation and ordinary state updates accumulate senders and expiry loops that retain old documents.
Capture the interval and timer handles, remove the exact pointer listener, cancel pending callbacks, and close the matching connection once.
7. High-frequency pointer coordinates are stored in render state
Location: src/use-cursor-presence.tsx:16-17,45-48
The local coordinate is not rendered, yet every pointer event calls setLocalCursor, rerendering the presence layer at device input frequency. This also feeds the reconnect defect through the dependency array.
Keep transient outbound coordinates in a ref and reserve React state for participant data that changes the rendered UI.
Reasonable non-findings
- Rendering remote cursors from React state is appropriate because those values affect visible output.
- Keying cursor elements by participant ID is stable under the stated one-cursor-per-participant model.
- Marking the visual cursor layer
aria-hidden is reasonable because it conveys transient pointer position rather than document content.
- A one-second expiry sweep is sufficiently precise for the specified 15-second timeout.