react-broadcast-sync 1.11.2 → 2.1.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.
@@ -0,0 +1,102 @@
1
+ ---
2
+ name: react-broadcast-sync
3
+ description: Use when adding cross-tab messaging or state sync to a React app with the react-broadcast-sync package (useBroadcastChannel, BroadcastProvider). Covers syncing state between tabs, giving late-opened tabs the current state, and common mistakes.
4
+ ---
5
+
6
+ # react-broadcast-sync
7
+
8
+ Cross-tab messaging for React over the browser `BroadcastChannel` API. Version 2.x. Same origin only.
9
+
10
+ ## What it is and is not
11
+
12
+ - `messages` is an event log of what OTHER tabs sent. It is not current state.
13
+ - A tab never receives its own messages.
14
+ - Messages are not replayed. A tab opened later sees only messages sent after it opened.
15
+ - Deduplication is by message ID, not content.
16
+ - There is no automatic state replication. You send messages and apply them yourself.
17
+ - Telemetry is off by default (since 2.0.0). Do not add `telemetry: true` unless the user asks for it.
18
+
19
+ ## Install
20
+
21
+ ```bash
22
+ npm install react-broadcast-sync
23
+ ```
24
+
25
+ ## Recipe 1: sync a piece of state between tabs
26
+
27
+ Update local state, then send one message from the same handler. Apply incoming messages with `onMessage`.
28
+
29
+ ```tsx
30
+ import { useState } from 'react';
31
+ import { useBroadcastChannel } from 'react-broadcast-sync';
32
+
33
+ function Dashboard() {
34
+ const [filters, setFilters] = useState({ status: 'all' });
35
+
36
+ const { postMessage } = useBroadcastChannel('dashboard', {
37
+ onMessage: {
38
+ 'filters-update': msg => setFilters(msg.message),
39
+ },
40
+ });
41
+
42
+ const updateFilters = (next: typeof filters) => {
43
+ setFilters(next);
44
+ postMessage('filters-update', next);
45
+ };
46
+
47
+ return <FilterBar value={filters} onChange={updateFilters} />;
48
+ }
49
+ ```
50
+
51
+ Do NOT call `postMessage` from a `useEffect` that watches state which incoming messages also set. Tabs re-broadcast what they receive and ping-pong forever.
52
+
53
+ ## Recipe 2: give a late-opened tab the current state
54
+
55
+ Because there is no replay, a new tab must ask and an existing tab must answer. Keep the current value in a ref so the handler does not read stale state.
56
+
57
+ ```tsx
58
+ const filtersRef = useRef(filters);
59
+ filtersRef.current = filters;
60
+
61
+ const { postMessage } = useBroadcastChannel('dashboard', {
62
+ onMessage: {
63
+ 'filters-update': msg => setFilters(msg.message),
64
+ 'state-request': () => postMessage('filters-update', filtersRef.current),
65
+ },
66
+ });
67
+
68
+ useEffect(() => {
69
+ postMessage('state-request', null);
70
+ }, []);
71
+ ```
72
+
73
+ If no other tab is open, nobody answers. Start from sensible defaults.
74
+
75
+ ## Recipe 3: latest value only
76
+
77
+ `keepLatestMessage: true` keeps ONE slot across all types. A later message of any type replaces the earlier one. It does not replay to late tabs. For latest-per-type, use separate channels or `namespace`, or read with `getLatestMessage({ type })` without `keepLatestMessage`.
78
+
79
+ ## Recipe 4: stable sourceName
80
+
81
+ Leave `sourceName` unset unless you need a stable name. A unique one is generated per tab. If you set it:
82
+
83
+ - It must be unique per tab. A tab ignores messages whose `source` equals its own `sourceName`, so two tabs with the same name never hear each other.
84
+ - Do not build it inline (for example `crypto.randomUUID()` in the options). It would change every render. Create it once with `useRef` or `useState`.
85
+
86
+ ## Recipe 5: several topics
87
+
88
+ Use one channel per topic (or `namespace`) so each listener only gets its own messages. `registeredTypes` limits accepted message types.
89
+
90
+ ## Common mistakes
91
+
92
+ - Treating `messages` as state. Use `useState` plus `onMessage`.
93
+ - Sending from `useEffect` on synced state (loop, see Recipe 1).
94
+ - Expecting a new tab to get past messages (see Recipe 2).
95
+ - Using the same `sourceName` in every tab.
96
+ - `cleanupDebounceMs` larger than `cleaningInterval`, which can stop expired messages from being removed.
97
+ - Expecting cross-origin delivery. `BroadcastChannel` is same origin only.
98
+ - Enabling telemetry without being asked.
99
+
100
+ ## More
101
+
102
+ Full docs: https://github.com/IdanShalem/react-broadcast-sync#readme