react-x11 2.52.0 → 2.53.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 +10 -10
- package/docs/README.md +160 -314
- package/docs/accessibility.md +176 -229
- package/docs/appearance.md +77 -176
- package/docs/click-to-component.md +18 -51
- package/docs/clipboard.md +40 -59
- package/docs/components.md +518 -1036
- package/docs/dbus.md +61 -133
- package/docs/debugging.md +152 -245
- package/docs/desktop-calendar.md +51 -88
- package/docs/desktop.md +222 -347
- package/docs/devtools.md +50 -167
- package/docs/drag-and-drop.md +126 -204
- package/docs/ecosystem/animation.md +91 -125
- package/docs/ecosystem/data-fetching.md +38 -53
- package/docs/ecosystem/dev-tooling.md +159 -202
- package/docs/ecosystem/forms.md +61 -97
- package/docs/ecosystem/headless.md +64 -89
- package/docs/ecosystem/i18n.md +18 -21
- package/docs/ecosystem/icons.md +93 -126
- package/docs/ecosystem/layout.md +42 -52
- package/docs/ecosystem/routing.md +18 -29
- package/docs/ecosystem/state.md +71 -88
- package/docs/ecosystem/testing.md +131 -157
- package/docs/ecosystem/theming.md +119 -165
- package/docs/ecosystem.md +145 -179
- package/docs/elements.md +1091 -1774
- package/docs/embedding.md +83 -132
- package/docs/events.md +412 -695
- package/docs/extending.md +784 -1326
- package/docs/eyedropper.md +63 -122
- package/docs/filedialog.md +71 -107
- package/docs/frame.md +195 -339
- package/docs/gl.md +65 -62
- package/docs/globalmenu.md +63 -146
- package/docs/macos.md +531 -3234
- package/docs/notifications.md +33 -47
- package/docs/packaging.md +166 -309
- package/docs/permissions.md +9 -14
- package/docs/react-features.md +81 -106
- package/docs/remote.md +32 -76
- package/docs/scale.md +72 -186
- package/docs/styling.md +329 -547
- package/docs/system.md +200 -280
- package/docs/testing.md +63 -83
- package/docs/typescript.md +6 -48
- package/docs/uri-schemes.md +67 -107
- package/docs/wayland-backend.md +200 -459
- package/docs/windows-integrations.md +75 -131
- package/docs/windows.md +343 -1367
- package/package.json +3 -2
- package/src/Reconciler.js +2 -2
- package/src/a11y.js +1 -1
- package/src/backend/context2d.js +3 -3
- package/src/bootstrap.js +1 -1
- package/src/cocoa/app.js +6 -6
- package/src/cocoa/bezels.js +1 -1
- package/src/cocoa/fonts.js +1 -1
- package/src/cocoa/globalmenu.js +1 -1
- package/src/cocoa/keymap.js +1 -1
- package/src/cocoa/panewindow.js +1 -1
- package/src/cocoa/player.js +1 -1
- package/src/cocoa/presenter.js +2 -2
- package/src/cocoa/promotion.js +1 -1
- package/src/cocoa/relaunch.js +1 -1
- package/src/cocoa/threaded.js +1 -1
- package/src/cocoa/video.js +1 -1
- package/src/cocoa/window.js +4 -4
- package/src/components/Menu.js +7 -2
- package/src/components/Switch.js +1 -1
- package/src/components/native.js +1 -1
- package/src/dnd.js +2 -2
- package/src/glnodes.js +1 -1
- package/src/grid.js +1 -1
- package/src/layouts.js +1 -1
- package/src/nodes/animation.js +1 -1
- package/src/nodes/invalidate.js +1 -1
- package/src/nodes/layouthost.js +1 -1
- package/src/nodes/video.js +19 -8
- package/src/nodes/window/droptarget.js +1 -1
- package/src/pacing.js +1 -1
- package/src/refresh/index.js +2 -1
- package/src/types/video.d.ts +5 -2
- package/src/videobackend.js +28 -0
- package/src/videoframes.js +29 -16
- package/src/wayland/app.js +27 -0
- package/src/wayland/video.js +376 -0
- package/src/win32/a11y.js +1 -1
- package/src/win32/app.js +2 -2
- package/src/win32/fonts.js +1 -1
- package/src/win32/glarea.js +4 -4
- package/src/win32/overlay.js +1 -1
- package/src/win32/panewindow.js +3 -3
- package/src/win32/shell.js +1 -1
- package/src/win32/window.js +2 -2
- package/src/windowid.js +1 -1
- package/src/xvideo.js +189 -0
- package/docs/architecture/accessibility.md +0 -470
- package/docs/architecture/animation.md +0 -707
- package/docs/architecture/container-queries.md +0 -525
- package/docs/architecture/custom-layout.md +0 -334
- package/docs/architecture/drag-and-drop.md +0 -941
- package/docs/architecture/element-layer-contents.md +0 -622
- package/docs/architecture/frame-pacing.md +0 -255
- package/docs/architecture/grid-layout.md +0 -358
- package/docs/architecture/protocol-efficiency.md +0 -357
- package/docs/architecture/video.md +0 -1149
- package/docs/architecture/windowed-regions.md +0 -854
- package/docs/glx.md +0 -137
- package/docs/wayland.md +0 -545
- package/docs/windows-embedding.md +0 -271
- package/docs/windows-gl.md +0 -221
|
@@ -1,41 +1,36 @@
|
|
|
1
1
|
# Animation
|
|
2
2
|
|
|
3
|
-
react-x11
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
3
|
+
react-x11's built-in `transition` style property has a fixed easing curve
|
|
4
|
+
([styling](../styling.md#transitions)). This page covers what it cannot
|
|
5
|
+
express: springs with velocity carry-over, interruption, arbitrary easing
|
|
6
|
+
curves, shape morphing, and keeping an element mounted while it animates
|
|
7
|
+
out.
|
|
8
8
|
|
|
9
|
-
There is
|
|
10
|
-
|
|
11
|
-
|
|
9
|
+
**There is no `opacity` style property**, and `validateStyle` throws on
|
|
10
|
+
unknown properties, so the classic fade does not port; animate a colour, a
|
|
11
|
+
size or a position instead.
|
|
12
12
|
|
|
13
13
|
Two patterns recur. The cheap one is `onUpdate → setState → style prop`:
|
|
14
|
-
|
|
15
|
-
partial repaint of the node
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
`
|
|
25
|
-
|
|
26
|
-
`framer-motion` is not on this page. Both its component layer and its bare
|
|
27
|
-
value layer fail — see the
|
|
28
|
-
[negative-results register](../ecosystem.md#render-time), which also explains
|
|
29
|
-
why reaching past `motion.*` does not help.
|
|
14
|
+
each frame is a React render plus a commit, which react-x11 turns into a
|
|
15
|
+
partial repaint of the changed node. Keep the animated component a leaf — a
|
|
16
|
+
`<box>`, not the window root — and it holds spring frame rates. The other is
|
|
17
|
+
the [react-spring host](#react-spring): more setup, but it applies frames
|
|
18
|
+
imperatively and bypasses React.
|
|
19
|
+
|
|
20
|
+
Node has no global `requestAnimationFrame`. In an app, use the ntk window's
|
|
21
|
+
`requestAnimationFrame` (through a ref on `<window>`), which paces frames to
|
|
22
|
+
X connection latency; a 16 ms `setInterval` works everywhere else.
|
|
23
|
+
|
|
24
|
+
`framer-motion` fails at both its component and its value layer — see the
|
|
25
|
+
[negative-results register](../ecosystem.md#render-time).
|
|
30
26
|
|
|
31
27
|
## popmotion {#popmotion}
|
|
32
28
|
|
|
33
29
|
**Out of the box.** popmotion@11.0.5.
|
|
34
30
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
springs, with velocity and overshoot, without adopting the react-spring host.
|
|
31
|
+
`animate()` tweens or springs a plain JS value and calls `onUpdate(v)`
|
|
32
|
+
every frame. The smallest way to get real springs, with velocity and
|
|
33
|
+
overshoot, without the react-spring host.
|
|
39
34
|
|
|
40
35
|
```jsx
|
|
41
36
|
import React, { useEffect, useState } from 'react';
|
|
@@ -57,29 +52,25 @@ function Bar() {
|
|
|
57
52
|
}
|
|
58
53
|
```
|
|
59
54
|
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
back with the ntk window's clock.
|
|
55
|
+
With no `requestAnimationFrame`, popmotion falls back to a 16.7 ms
|
|
56
|
+
`setTimeout`, so it needs no configuration. For latency-adaptive pacing,
|
|
57
|
+
pass `animate()` a custom `driver` backed by the ntk window's clock.
|
|
64
58
|
|
|
65
59
|
- Springs on layout properties (width, margin) cost a yoga layout pass per
|
|
66
|
-
frame
|
|
67
|
-
- `animate`
|
|
68
|
-
`rgba(...)` strings it emits
|
|
69
|
-
-
|
|
70
|
-
|
|
60
|
+
frame, as react-x11's own layout transitions do.
|
|
61
|
+
- `animate` interpolates colour strings (`'#f00' → '#00f'`), and the
|
|
62
|
+
`rgba(...)` strings it emits paint as-is.
|
|
63
|
+
- Use only the value APIs — `animate`, `spring`, `decay`, `mix` — not
|
|
64
|
+
`styler` or the DOM helpers.
|
|
71
65
|
|
|
72
66
|
## d3-ease {#d3-ease}
|
|
73
67
|
|
|
74
68
|
**Out of the box.** d3-ease@3.0.1.
|
|
75
69
|
|
|
76
|
-
Pure easing functions
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
Compute `t` from wall time, pass `ease(t)` to an interpolator, push the
|
|
82
|
-
result into a style prop.
|
|
70
|
+
Pure easing functions — `ease(t)` maps normalized time to progress — with
|
|
71
|
+
thirty-plus curves (cubic, elastic, bounce, back) the built-in `transition`
|
|
72
|
+
lacks. Compute `t` from wall time, pass `ease(t)` to an interpolator, push
|
|
73
|
+
the result into a style prop.
|
|
83
74
|
|
|
84
75
|
```jsx
|
|
85
76
|
import React, { useEffect, useState } from 'react';
|
|
@@ -109,26 +100,19 @@ function Gauge() {
|
|
|
109
100
|
}
|
|
110
101
|
```
|
|
111
102
|
|
|
112
|
-
- ESM-only since v3 — `require()
|
|
113
|
-
ESM anyway.
|
|
103
|
+
- ESM-only since v3 — `import`, not `require()`.
|
|
114
104
|
- It is only the curve; you own the clock and the interpolation.
|
|
115
|
-
- Overshooting curves (elastic, back)
|
|
116
|
-
|
|
117
|
-
react-x11 validates as non-negative.
|
|
105
|
+
- Overshooting curves (elastic, back) leave the from/to range; clamp before
|
|
106
|
+
feeding anything react-x11 validates as non-negative.
|
|
118
107
|
|
|
119
108
|
## d3-interpolate {#d3-interpolate}
|
|
120
109
|
|
|
121
110
|
**Out of the box.** d3-interpolate@3.0.1.
|
|
122
111
|
|
|
123
112
|
`interpolate(a, b)` returns `t => value` for numbers, colours in any CSS
|
|
124
|
-
space, strings with embedded numbers, dates, arrays and nested objects.
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
Worth knowing: `interpolateRgb` emits `'rgb(r, g, b)'` strings and the paint
|
|
128
|
-
path takes them as-is, so colour animation needs no conversion. react-x11 has
|
|
129
|
-
an internal interpolator for its own `transition` styles, but it is not
|
|
130
|
-
exported and handles only what transitions need — d3-interpolate is the
|
|
131
|
-
general tool for hand-rolled timelines.
|
|
113
|
+
space, strings with embedded numbers, dates, arrays and nested objects.
|
|
114
|
+
`interpolateRgb` emits `'rgb(r, g, b)'` strings, which paint as-is. (The
|
|
115
|
+
interpolator behind react-x11's `transition` is not exported.)
|
|
132
116
|
|
|
133
117
|
```jsx
|
|
134
118
|
import { easeCubicOut } from 'd3-ease';
|
|
@@ -148,9 +132,8 @@ function Gauge() {
|
|
|
148
132
|
|
|
149
133
|
- ESM-only since v3.
|
|
150
134
|
- Generic `interpolate(a, b)` on objects interpolates _every_ numeric-looking
|
|
151
|
-
key,
|
|
152
|
-
|
|
153
|
-
properties you mean.
|
|
135
|
+
key, giving fractional values to properties that want integers.
|
|
136
|
+
Interpolate only the properties you mean.
|
|
154
137
|
- There is no `opacity`; to fade, interpolate a colour's alpha into
|
|
155
138
|
`backgroundColor: 'rgba(...)'`.
|
|
156
139
|
- `interpolateArray` over polygon points feeding `<canvas onDraw>` is a good
|
|
@@ -160,14 +143,11 @@ function Gauge() {
|
|
|
160
143
|
|
|
161
144
|
**Out of the box.** @tweenjs/tween.js@25.0.0.
|
|
162
145
|
|
|
163
|
-
`new Tween(obj).to(target, ms).easing(...).onUpdate(...).start()`,
|
|
164
|
-
_you_ call `tween.update(time)` on
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
window's `requestAnimationFrame`, which paces updates to X connection
|
|
169
|
-
latency. Use one `Group` per window if you run many tweens, so
|
|
170
|
-
`group.update(now)` steps them all from the same tick.
|
|
146
|
+
`new Tween(obj).to(target, ms).easing(...).onUpdate(...).start()`, then
|
|
147
|
+
_you_ call `tween.update(time)` on your own clock — there is no implicit
|
|
148
|
+
global loop. In an app, pump from the ntk window's `requestAnimationFrame`;
|
|
149
|
+
with many tweens, one `Group` per window lets `group.update(now)` step them
|
|
150
|
+
all from the same tick.
|
|
171
151
|
|
|
172
152
|
```jsx
|
|
173
153
|
import React, { useEffect, useState } from 'react';
|
|
@@ -200,11 +180,10 @@ function Panel() {
|
|
|
200
180
|
}
|
|
201
181
|
```
|
|
202
182
|
|
|
203
|
-
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
the classic first bug.
|
|
183
|
+
- Numbers only, including nested in objects and arrays. For colours use
|
|
184
|
+
d3-interpolate or popmotion.
|
|
185
|
+
- Nothing animates until something calls `update()` — the classic first
|
|
186
|
+
bug.
|
|
208
187
|
- `onUpdate` mutates the source object, so spread it into fresh state
|
|
209
188
|
(`setPos({ ...state })`) or React skips the re-render on identity.
|
|
210
189
|
|
|
@@ -212,11 +191,9 @@ function Panel() {
|
|
|
212
191
|
|
|
213
192
|
**Out of the box.** flubber@0.4.2.
|
|
214
193
|
|
|
215
|
-
Shape
|
|
194
|
+
Shape morphing for SVG paths: `interpolate(from, to)` returns
|
|
216
195
|
`t => d-string`, handling ring alignment, point insertion and topology
|
|
217
|
-
changes so intermediate frames
|
|
218
|
-
Shape morphing is the one animation the `<svg>` host makes easy, and flubber
|
|
219
|
-
is the standard tool for it.
|
|
196
|
+
changes so intermediate frames do not self-intersect.
|
|
220
197
|
|
|
221
198
|
```jsx
|
|
222
199
|
import React, { useEffect, useState } from 'react';
|
|
@@ -247,12 +224,12 @@ function Morph() {
|
|
|
247
224
|
|
|
248
225
|
- `import { interpolate } from 'flubber'` throws — it is a CJS package, so
|
|
249
226
|
use the default-import form above.
|
|
250
|
-
- Build the interpolator once (module scope or `useMemo`)
|
|
251
|
-
the expensive
|
|
227
|
+
- Build the interpolator once (module scope or `useMemo`); construction is
|
|
228
|
+
the expensive part.
|
|
252
229
|
- Every frame is a full SVG parse and rasterize of the `source` string. Fine
|
|
253
|
-
for icons and small figures; for large scenes
|
|
254
|
-
|
|
255
|
-
-
|
|
230
|
+
for icons and small figures; for large scenes draw the interpolated points
|
|
231
|
+
in `<canvas onDraw>`.
|
|
232
|
+
- 0.4.2 since 2017 — finished rather than unmaintained.
|
|
256
233
|
|
|
257
234
|
## react-transition-group {#react-transition-group}
|
|
258
235
|
|
|
@@ -260,16 +237,12 @@ function Morph() {
|
|
|
260
237
|
|
|
261
238
|
A transition _state machine_, not an animator: `<Transition in={bool}
|
|
262
239
|
timeout={ms}>` walks its child through `entering`/`entered`/`exiting`/
|
|
263
|
-
`exited` on a timer and keeps
|
|
264
|
-
|
|
265
|
-
while it animates out, which react-x11 has no built-in answer for.
|
|
240
|
+
`exited` on a timer and keeps it mounted during exit — the way to keep an
|
|
241
|
+
element mounted while it animates out, which react-x11 has no built-in for.
|
|
266
242
|
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
`transition` style so the state flip _tweens_ instead of snapping. That
|
|
271
|
-
combination is the whole "CSSTransition" story here, since there are no class
|
|
272
|
-
names.
|
|
243
|
+
Always pass `nodeRef` — without it, v4 falls back to `ReactDOM.findDOMNode`,
|
|
244
|
+
which cannot handle a react-x11 node. Map states to styles yourself, with the
|
|
245
|
+
built-in `transition` style so the flip _tweens_ instead of snapping.
|
|
273
246
|
|
|
274
247
|
```jsx
|
|
275
248
|
import React, { useRef } from 'react';
|
|
@@ -301,14 +274,13 @@ function Panel({ shown }) {
|
|
|
301
274
|
}
|
|
302
275
|
```
|
|
303
276
|
|
|
304
|
-
- **`react-dom` must be
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
them equal, or the state flips mid-tween.
|
|
277
|
+
- **`react-dom` must be in `node_modules`.** `cjs/Transition.js` requires it
|
|
278
|
+
at module scope for the `findDOMNode` fallback, even when `nodeRef` means
|
|
279
|
+
it is never called. Install the peer; nothing renders through it.
|
|
280
|
+
- `CSSTransition` toggles class names, and there are none. Use plain
|
|
281
|
+
`Transition`.
|
|
282
|
+
- `timeout` and your transition duration are independent; keep them equal,
|
|
283
|
+
or the state flips mid-tween.
|
|
312
284
|
- `addEndListener` works; call the provided `done` yourself.
|
|
313
285
|
|
|
314
286
|
## `@react-spring/core` {#react-spring}
|
|
@@ -316,23 +288,20 @@ function Panel({ shown }) {
|
|
|
316
288
|
**Adapter, ~20 lines.** @react-spring/core@10.1.2,
|
|
317
289
|
@react-spring/animated@10.1.2.
|
|
318
290
|
|
|
319
|
-
The platform-independent half of react-spring: `useSpring`,
|
|
320
|
-
`useTrail`, `SpringValue`, `Controller
|
|
321
|
-
|
|
322
|
-
`@react-spring/
|
|
323
|
-
|
|
324
|
-
Springs, interruption with velocity carry-over, and `api.start()` are exactly
|
|
325
|
-
what the built-in duration-only `transition` cannot express. Two public
|
|
291
|
+
The platform-independent half of react-spring: `useSpring`,
|
|
292
|
+
`useTransition`, `useTrail`, `SpringValue`, `Controller` — springs,
|
|
293
|
+
interruption with velocity carry-over, `api.start()`.
|
|
294
|
+
`@react-spring/animated` provides `createHost`, the documented seam for a
|
|
295
|
+
non-DOM target (how `@react-spring/native` and `/three` exist). Two public
|
|
326
296
|
seams do the work:
|
|
327
297
|
|
|
328
|
-
- `Globals.assign({ now, requestAnimationFrame })` points
|
|
329
|
-
|
|
298
|
+
- `Globals.assign({ now, requestAnimationFrame })` points the frame loop at
|
|
299
|
+
a clock.
|
|
330
300
|
- `createHost(['box', …], { applyAnimatedValues })` produces `animated.box`.
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
once while hundreds of frames apply.
|
|
301
|
+
Each frame, react-spring calls `applyAnimatedValues(instance, props)` with
|
|
302
|
+
the react-x11 node, and `node.applyProps(next, prev)` is the imperative
|
|
303
|
+
path the reconciler's `commitUpdate` uses. A component renders once while
|
|
304
|
+
hundreds of frames apply.
|
|
336
305
|
|
|
337
306
|
```jsx
|
|
338
307
|
// spring-x11.js — the adapter
|
|
@@ -375,20 +344,17 @@ function Bar() {
|
|
|
375
344
|
}
|
|
376
345
|
```
|
|
377
346
|
|
|
378
|
-
- **Merge `style` one level deep in `applyAnimatedValues`** —
|
|
379
|
-
number-one silent failure.
|
|
380
|
-
`props.getValue(true)`
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
CSS colours are not enabled by default in `@react-spring/shared` — stick to
|
|
386
|
-
hex and rgb.
|
|
347
|
+
- **Merge `style` one level deep in `applyAnimatedValues`** — the
|
|
348
|
+
number-one silent failure. Frames carry _animated leaves only_
|
|
349
|
+
(`props.getValue(true)`), so static keys (`height: 30` beside an animated
|
|
350
|
+
`width`) are absent, and a naive `{...prev, ...props}` merge wipes them:
|
|
351
|
+
the box paints nothing, with no error.
|
|
352
|
+
- Hex, `rgb()` and `rgba()` strings interpolate; named CSS colours are not
|
|
353
|
+
enabled by default in `@react-spring/shared`.
|
|
387
354
|
- Do not use `useScroll`, `useResize`, `useInView` or `useReducedMotion` from
|
|
388
355
|
core; they observe the DOM `window` and `matchMedia`.
|
|
389
356
|
`Globals.assign({ skipAnimation })` is the manual reduced-motion switch.
|
|
390
357
|
- Style arrays (`style={[a, b]}`) defeat the shallow merge. Flatten first —
|
|
391
358
|
react-x11 exports `flattenStyle`.
|
|
392
|
-
-
|
|
393
|
-
|
|
394
|
-
clock or a 16 ms timeout.
|
|
359
|
+
- A `setImmediate` clock runs frames as fast as the event loop turns — fine
|
|
360
|
+
for tests, wasteful for apps. Use the ntk window clock or a 16 ms timeout.
|
|
@@ -1,33 +1,26 @@
|
|
|
1
1
|
# Data fetching
|
|
2
2
|
|
|
3
3
|
A react-x11 app is an ordinary Node process, so `fetch`, undici, `ky` and
|
|
4
|
-
`graphql-request`
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
That check is the whole story on this page. Get it wrong and nothing throws;
|
|
10
|
-
the app just stops refetching.
|
|
4
|
+
`graphql-request` work unmodified. The _cache_ layer on top needs care:
|
|
5
|
+
stale-while-revalidate libraries decide whether they are "on a server" by
|
|
6
|
+
looking for a global `window`, and a react-x11 process has none. Get it
|
|
7
|
+
wrong and nothing throws; the app just stops refetching.
|
|
11
8
|
|
|
12
9
|
## TanStack Query {#tanstack-query}
|
|
13
10
|
|
|
14
11
|
**Adapter, ~12 lines.** @tanstack/react-query@5.101.4 (query-core 5.101.4).
|
|
15
12
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
`environmentManager` — all replaceable.
|
|
21
|
-
|
|
22
|
-
The trap is query-core's environment probe:
|
|
13
|
+
`useQuery` deduplicates, caches, retries and background-refetches;
|
|
14
|
+
`useMutation` handles writes with invalidation. The environment coupling
|
|
15
|
+
lives in three replaceable managers — `focusManager`, `onlineManager`,
|
|
16
|
+
`environmentManager`. The trap is query-core's probe:
|
|
23
17
|
|
|
24
18
|
```js
|
|
25
19
|
// @tanstack/query-core src/utils.ts
|
|
26
20
|
export const isServer = typeof window === 'undefined' || 'Deno' in globalThis;
|
|
27
21
|
```
|
|
28
22
|
|
|
29
|
-
|
|
30
|
-
Concretely:
|
|
23
|
+
With no `window`, query-core decides it is doing SSR:
|
|
31
24
|
|
|
32
25
|
- `retry` defaults to **0** instead of 3;
|
|
33
26
|
- stale timers are never scheduled and `refetchInterval` is disabled;
|
|
@@ -37,10 +30,8 @@ Concretely:
|
|
|
37
30
|
`window.addEventListener('visibilitychange')`, so focus refetching is
|
|
38
31
|
inert.
|
|
39
32
|
|
|
40
|
-
The
|
|
41
|
-
|
|
42
|
-
focus events, which fire on the X `FocusIn`/`FocusOut` the window manager
|
|
43
|
-
delivers.
|
|
33
|
+
The fix is `environmentManager.setIsServer()`, re-exported from
|
|
34
|
+
`@tanstack/react-query`, paired with react-x11's window focus events:
|
|
44
35
|
|
|
45
36
|
```jsx
|
|
46
37
|
// query-x11.js — the whole adapter
|
|
@@ -89,29 +80,24 @@ function Weather() {
|
|
|
89
80
|
);
|
|
90
81
|
```
|
|
91
82
|
|
|
92
|
-
Focusing the window
|
|
93
|
-
|
|
83
|
+
Focusing the window now refetches stale queries, like refocusing a browser
|
|
84
|
+
tab.
|
|
94
85
|
|
|
95
86
|
- **Call `setIsServer` before creating the `QueryClient`** — the managers
|
|
96
87
|
are module singletons.
|
|
97
|
-
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
- `onlineManager` defaults to online under Node (there is no
|
|
102
|
-
`navigator.onLine`). Wire it to NetworkManager over D-Bus if you want real
|
|
103
|
-
offline handling.
|
|
88
|
+
- Switching between two of your own windows fires blur-then-focus, which
|
|
89
|
+
refetches stale queries; raise `staleTime` if that is too eager.
|
|
90
|
+
- `onlineManager` defaults to online under Node (no `navigator.onLine`).
|
|
91
|
+
Wire it to NetworkManager over D-Bus for real offline handling.
|
|
104
92
|
- `@tanstack/react-query-devtools` is DOM-rendered and not usable.
|
|
105
93
|
|
|
106
94
|
## SWR {#swr}
|
|
107
95
|
|
|
108
96
|
**Adapter, 1 line — but read the dead end first.** swr@2.4.2.
|
|
109
97
|
|
|
110
|
-
|
|
111
|
-
and
|
|
112
|
-
|
|
113
|
-
Focus and reconnect revalidation do not, and they fail silently. SWR
|
|
114
|
-
computes its environment once at import time, with no override:
|
|
98
|
+
`useSWR` fetches on mount, caches, dedupes and re-renders unmodified. Focus
|
|
99
|
+
and reconnect revalidation fail silently: SWR computes its environment once
|
|
100
|
+
at import time, with no override:
|
|
115
101
|
|
|
116
102
|
```js
|
|
117
103
|
// swr/dist/_internal/config-context-*.mjs
|
|
@@ -121,14 +107,11 @@ const IS_SERVER = !isWindowDefined || isLegacyDeno;
|
|
|
121
107
|
|
|
122
108
|
Revalidation event sources are registered only `if (!IS_SERVER)` —
|
|
123
109
|
_including_ a custom `initFocus`/`initReconnect` passed through the
|
|
124
|
-
cache-provider config
|
|
125
|
-
|
|
126
|
-
is never called, and swr ends up with zero registered listeners. Unlike
|
|
127
|
-
TanStack Query there is no `setIsServer`.
|
|
110
|
+
cache-provider config, so the provider approach from SWR's docs is dead code
|
|
111
|
+
under Node and swr registers zero listeners. There is no `setIsServer`.
|
|
128
112
|
|
|
129
|
-
Two ways out. The simple one
|
|
130
|
-
|
|
131
|
-
so hang it off `<window onFocus>`:
|
|
113
|
+
Two ways out. The simple one: drive revalidation with `mutate`, whose key
|
|
114
|
+
filter `() => true` revalidates every cached key, from `<window onFocus>`:
|
|
132
115
|
|
|
133
116
|
```jsx
|
|
134
117
|
import React from 'react';
|
|
@@ -155,8 +138,8 @@ function Weather() {
|
|
|
155
138
|
);
|
|
156
139
|
```
|
|
157
140
|
|
|
158
|
-
The thorough one
|
|
159
|
-
|
|
141
|
+
The thorough one: make `IS_SERVER` false, which turns swr's own machinery
|
|
142
|
+
back on. It has to happen **before the first import of swr**:
|
|
160
143
|
|
|
161
144
|
```js
|
|
162
145
|
// swr-env.js — imported first, before anything that imports swr
|
|
@@ -178,24 +161,26 @@ export const fireFocus = () =>
|
|
|
178
161
|
(listeners.get('focus') ?? []).forEach((fn) => fn());
|
|
179
162
|
```
|
|
180
163
|
|
|
181
|
-
|
|
182
|
-
|
|
164
|
+
swr then registers `visibilitychange`, `focus`, `online` and `offline`
|
|
165
|
+
listeners and `revalidateOnFocus` behaves. Two gotchas:
|
|
183
166
|
|
|
184
167
|
- Fire only `'focus'`. An `'offline'` event sets swr's internal online flag
|
|
185
168
|
false and silently suppresses the deferred revalidation.
|
|
186
|
-
- `focusThrottleInterval` defaults to 5000 ms, so a focus
|
|
187
|
-
|
|
188
|
-
|
|
169
|
+
- `focusThrottleInterval` defaults to 5000 ms, so a focus within five
|
|
170
|
+
seconds of the last revalidation is dropped — which looks exactly like the
|
|
171
|
+
shim not working. Lower it while testing the wiring.
|
|
172
|
+
|
|
173
|
+
Other caveats:
|
|
189
174
|
|
|
190
175
|
- **`revalidateOnFocus` and `revalidateOnReconnect` are inert** unless you
|
|
191
176
|
define the globals. Nothing warns you.
|
|
192
|
-
- The global `mutate`
|
|
193
|
-
|
|
194
|
-
|
|
177
|
+
- The global `mutate` targets the _default_ cache, so with a custom
|
|
178
|
+
`provider` via `<SWRConfig>` it silently misses; use
|
|
179
|
+
`useSWRConfig().mutate` from inside the tree.
|
|
195
180
|
- Suspense mode throws
|
|
196
181
|
`Fallback data is required when using Suspense in SSR.` because SWR
|
|
197
182
|
believes it is on a server. Provide `fallbackData`, or avoid
|
|
198
183
|
`suspense: true`.
|
|
199
184
|
- `preload()` is an SSR-gated no-op under Node.
|
|
200
|
-
-
|
|
201
|
-
|
|
185
|
+
- For focus revalidation with retries and cache GC done properly, prefer
|
|
186
|
+
TanStack Query — it ships a supported environment override.
|