use-everywhere 0.11.0 → 0.11.2
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/llms.txt +113 -0
- package/package.json +23 -5
package/llms.txt
ADDED
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
# use-everywhere
|
|
2
|
+
|
|
3
|
+
> React hooks for state, messages, presence and leader election that exist in
|
|
4
|
+
> every tab, window and worker on an origin. `useSharedState` is `useState` whose
|
|
5
|
+
> value lives in every tab; writes converge in milliseconds and a tab opened
|
|
6
|
+
> later hydrates to the current value rather than the initial one. There is no
|
|
7
|
+
> Provider and no server. This package re-exports the whole of
|
|
8
|
+
> `@use-everywhere/core`, so it is the only install a React app needs. React >=
|
|
9
|
+
> 18. Ships ESM and CommonJS.
|
|
10
|
+
|
|
11
|
+
## Install
|
|
12
|
+
|
|
13
|
+
npm install use-everywhere
|
|
14
|
+
|
|
15
|
+
`react >= 18` is the only peer dependency.
|
|
16
|
+
|
|
17
|
+
## Minimal working example
|
|
18
|
+
|
|
19
|
+
```tsx
|
|
20
|
+
import { useSharedState } from 'use-everywhere';
|
|
21
|
+
|
|
22
|
+
export function Counter() {
|
|
23
|
+
// Same signature as useState. The value exists in every tab on the origin.
|
|
24
|
+
const [count, setCount] = useSharedState('count', 0);
|
|
25
|
+
|
|
26
|
+
return <button onClick={() => setCount((n) => n + 1)}>{count}</button>;
|
|
27
|
+
}
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
Open it in two tabs. Clicking in one updates both.
|
|
31
|
+
|
|
32
|
+
## The three mistakes to avoid
|
|
33
|
+
|
|
34
|
+
**1. Calling `defineChannel` or `defineStore` inside a component.** They are
|
|
35
|
+
module-scope factories. Called in a render they build a new bus on every
|
|
36
|
+
render, and the page syncs nothing while looking completely correct. Install
|
|
37
|
+
`eslint-plugin-use-everywhere` — this is the mistake its `define-at-module-scope`
|
|
38
|
+
rule exists for.
|
|
39
|
+
|
|
40
|
+
```tsx
|
|
41
|
+
// WRONG — a new channel per render
|
|
42
|
+
function Cart() {
|
|
43
|
+
const shop = defineChannel<ShopEvents>('shop');
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// RIGHT — once, at module level
|
|
47
|
+
const shop = defineChannel<ShopEvents>('shop');
|
|
48
|
+
function Cart() {
|
|
49
|
+
const post = shop.useSend();
|
|
50
|
+
}
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
**2. Expecting a Provider.** There isn't one, and there is nothing to wrap. A
|
|
54
|
+
BroadcastChannel is already global to the origin, so identity is the name string
|
|
55
|
+
and the hooks read module-level singletons keyed by it. Just call them.
|
|
56
|
+
|
|
57
|
+
**3. Putting a value in shared state that structured clone cannot carry.** The
|
|
58
|
+
wire is the structured clone algorithm, so functions, class instances, DOM nodes
|
|
59
|
+
and symbols do not survive. Dates, Maps, Sets and typed arrays do. The
|
|
60
|
+
`structured-clone-safe` lint rule catches the rest.
|
|
61
|
+
|
|
62
|
+
## API
|
|
63
|
+
|
|
64
|
+
| Export | Kind | What it answers |
|
|
65
|
+
| --- | --- | --- |
|
|
66
|
+
| `useSharedState` | hook | "What is the current value?" — `useState` across every tab |
|
|
67
|
+
| `useSharedReducer` | hook | The same, dispatch-shaped |
|
|
68
|
+
| `useSharedStore` | hook | Subscribe to a slice of a store with a selector |
|
|
69
|
+
| `useChannel` | hook | The typed event channel for a name |
|
|
70
|
+
| `useMessage` | hook | Run a handler when another tab posts an event |
|
|
71
|
+
| `useSend` | hook | A stable, typed `post` for a channel |
|
|
72
|
+
| `useAsk` | hook | Request/response across tabs |
|
|
73
|
+
| `useAnswer` | hook | Serve the other half of `useAsk` |
|
|
74
|
+
| `usePeers` | hook | A live list of open tabs, windows and workers |
|
|
75
|
+
| `useClientId` | hook | This tab's stable id on a bus |
|
|
76
|
+
| `usePresenceMetadata` | hook | Attach and read per-peer metadata |
|
|
77
|
+
| `useLeader` | hook | Elect exactly one tab to own a job |
|
|
78
|
+
| `useIsLeader` | hook | Whether this tab currently holds the lease |
|
|
79
|
+
| `useLeaderEffect` | hook | Run an effect only in the leader tab |
|
|
80
|
+
| `useOpenedWindow` | hook | The cross-origin window lifecycle as render state |
|
|
81
|
+
| `useHydrated` | hook | Whether the first cross-tab sync has landed (SSR) |
|
|
82
|
+
| `defineChannel` | factory | Bind a channel name + message map once, at module scope |
|
|
83
|
+
| `defineStore` | factory | The same for a store, and where persistence is enabled |
|
|
84
|
+
| `createNamespace` | factory | Prefix every bus name, for micro-frontends |
|
|
85
|
+
| `Inspector` | component | Devtools panel; import from `use-everywhere/devtools` |
|
|
86
|
+
|
|
87
|
+
`useSharedState` and the presence hooks are the two most people need. Everything
|
|
88
|
+
in `@use-everywhere/core` is re-exported from here too — see that package's
|
|
89
|
+
`llms.txt` for the framework-free engines.
|
|
90
|
+
|
|
91
|
+
## Gotchas
|
|
92
|
+
|
|
93
|
+
- **Shared state never crosses origins.** Two origins are two trust domains. For
|
|
94
|
+
that, open a window explicitly with `useOpenedWindow`, which is typed,
|
|
95
|
+
per-message and validated by origin.
|
|
96
|
+
- **Last-writer-wins, per key.** Per-key `[counter, clientId]` clocks give a
|
|
97
|
+
deterministic tie-break, not a merge. Two tabs editing the same key
|
|
98
|
+
concurrently means one of them loses — use `useSharedReducer` when you need the
|
|
99
|
+
operations to compose instead.
|
|
100
|
+
- **The name is the identity.** `useSharedState('count', 0)` in two different
|
|
101
|
+
components is one value. Namespace with `createNamespace` if that is not what
|
|
102
|
+
you want.
|
|
103
|
+
- **SSR is safe.** Hooks render initial values on the server and converge after
|
|
104
|
+
hydration; nothing throws where `BroadcastChannel` is absent.
|
|
105
|
+
|
|
106
|
+
## Docs
|
|
107
|
+
|
|
108
|
+
- Full documentation: https://rxova.org/packages/use-everywhere/
|
|
109
|
+
- Agent-facing index: https://rxova.org/packages/use-everywhere/llms.txt
|
|
110
|
+
- Every page as raw markdown: add `.md` to any docs URL
|
|
111
|
+
- Hooks reference: https://rxova.org/packages/use-everywhere/hooks/overview.md
|
|
112
|
+
- Error codes: https://rxova.org/packages/use-everywhere/errors.md
|
|
113
|
+
- Source: https://github.com/rxova/use-everywhere
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "use-everywhere",
|
|
3
|
-
"version": "0.11.
|
|
3
|
+
"version": "0.11.2",
|
|
4
4
|
"description": "React hooks for state and messages shared across tabs, windows, and workers",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Jonatan Kruszewski <jonakrusze@gmail.com>",
|
|
@@ -9,17 +9,34 @@
|
|
|
9
9
|
"url": "git+https://github.com/rxova/use-everywhere.git",
|
|
10
10
|
"directory": "packages/react"
|
|
11
11
|
},
|
|
12
|
-
"homepage": "https://
|
|
12
|
+
"homepage": "https://rxova.org/packages/use-everywhere/",
|
|
13
13
|
"bugs": "https://github.com/rxova/use-everywhere/issues",
|
|
14
14
|
"keywords": [
|
|
15
15
|
"react",
|
|
16
16
|
"hooks",
|
|
17
|
+
"react-hooks",
|
|
17
18
|
"broadcastchannel",
|
|
19
|
+
"broadcast-channel",
|
|
18
20
|
"cross-tab",
|
|
21
|
+
"multi-tab",
|
|
22
|
+
"tabs",
|
|
23
|
+
"tab-sync",
|
|
24
|
+
"sync",
|
|
19
25
|
"shared-state",
|
|
26
|
+
"state-management",
|
|
20
27
|
"postmessage",
|
|
21
28
|
"cross-origin",
|
|
22
|
-
"
|
|
29
|
+
"cross-window",
|
|
30
|
+
"presence",
|
|
31
|
+
"leader-election",
|
|
32
|
+
"cross-tab-state",
|
|
33
|
+
"tab-synchronization",
|
|
34
|
+
"broadcast",
|
|
35
|
+
"pubsub",
|
|
36
|
+
"web-locks",
|
|
37
|
+
"sharedworker",
|
|
38
|
+
"usestate",
|
|
39
|
+
"shared-worker"
|
|
23
40
|
],
|
|
24
41
|
"publishConfig": {
|
|
25
42
|
"access": "public"
|
|
@@ -75,7 +92,8 @@
|
|
|
75
92
|
}
|
|
76
93
|
},
|
|
77
94
|
"files": [
|
|
78
|
-
"dist"
|
|
95
|
+
"dist",
|
|
96
|
+
"llms.txt"
|
|
79
97
|
],
|
|
80
98
|
"size-limit": [
|
|
81
99
|
{
|
|
@@ -152,7 +170,7 @@
|
|
|
152
170
|
}
|
|
153
171
|
],
|
|
154
172
|
"dependencies": {
|
|
155
|
-
"@use-everywhere/core": "0.11.
|
|
173
|
+
"@use-everywhere/core": "0.11.2"
|
|
156
174
|
},
|
|
157
175
|
"peerDependencies": {
|
|
158
176
|
"react": ">=18",
|