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.
- package/README.md +155 -30
- package/TELEMETRY.md +13 -19
- package/dist/BroadcastProvider.d.ts +21 -2
- package/dist/hooks/useBroadcastChannel.d.ts +19 -1
- package/dist/index.cjs.js +1 -1
- package/dist/index.cjs.js.map +1 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.esm.js +1 -1
- package/dist/index.esm.js.map +1 -1
- package/dist/types/types.d.ts +8 -10
- package/llms.txt +367 -0
- package/package.json +16 -3
- package/skills/react-broadcast-sync/SKILL.md +102 -0
|
@@ -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
|