@effect/atom-react 4.0.0-beta.7 → 4.0.0-beta.71
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/dist/Hooks.d.ts +214 -13
- package/dist/Hooks.d.ts.map +1 -1
- package/dist/Hooks.js +199 -12
- package/dist/Hooks.js.map +1 -1
- package/dist/ReactHydration.d.ts +22 -2
- package/dist/ReactHydration.d.ts.map +1 -1
- package/dist/ReactHydration.js +40 -2
- package/dist/ReactHydration.js.map +1 -1
- package/dist/RegistryContext.d.ts +40 -3
- package/dist/RegistryContext.d.ts.map +1 -1
- package/dist/RegistryContext.js +63 -4
- package/dist/RegistryContext.js.map +1 -1
- package/dist/ScopedAtom.d.ts +49 -23
- package/dist/ScopedAtom.d.ts.map +1 -1
- package/dist/ScopedAtom.js +50 -12
- package/dist/ScopedAtom.js.map +1 -1
- package/dist/index.d.ts +5 -5
- package/dist/index.js +5 -5
- package/package.json +9 -9
- package/src/Hooks.ts +233 -14
- package/src/ReactHydration.ts +44 -3
- package/src/RegistryContext.ts +63 -4
- package/src/ScopedAtom.ts +67 -24
- package/src/index.ts +5 -5
package/src/Hooks.ts
CHANGED
|
@@ -1,5 +1,23 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* React hooks for reading, writing, mounting, refreshing, and subscribing to
|
|
3
|
+
* Effect atoms from the registry provided by `RegistryContext`.
|
|
4
|
+
*
|
|
5
|
+
* **Common tasks**
|
|
6
|
+
*
|
|
7
|
+
* - Read atom values in React components with {@link useAtomValue}
|
|
8
|
+
* - Read and write writable atoms with {@link useAtom}
|
|
9
|
+
* - Write without subscribing to the value with {@link useAtomSet}
|
|
10
|
+
* - Seed registry-local initial values with {@link useAtomInitialValues}
|
|
11
|
+
* - Integrate `AsyncResult` atoms with React Suspense through {@link useAtomSuspense}
|
|
12
|
+
* - Subscribe to atom changes or derive stable `AtomRef` properties
|
|
13
|
+
*
|
|
14
|
+
* **Gotchas**
|
|
15
|
+
*
|
|
16
|
+
* - Hooks use the current `RegistryContext`, so each provider has an independent atom registry
|
|
17
|
+
* - Writable atoms are mounted by the write-oriented hooks before updates are sent
|
|
18
|
+
* - Suspense support throws promises for initial or waiting `AsyncResult` values and defects for failures unless `includeFailure` is enabled
|
|
19
|
+
*
|
|
20
|
+
* @since 4.0.0
|
|
3
21
|
*/
|
|
4
22
|
"use client"
|
|
5
23
|
|
|
@@ -55,8 +73,20 @@ function useStore<A>(registry: AtomRegistry.AtomRegistry, atom: Atom.Atom<A>): A
|
|
|
55
73
|
const initialValuesSet = new WeakMap<AtomRegistry.AtomRegistry, WeakSet<Atom.Atom<any>>>()
|
|
56
74
|
|
|
57
75
|
/**
|
|
58
|
-
*
|
|
76
|
+
* Seeds initial atom values in the current React atom registry.
|
|
77
|
+
*
|
|
78
|
+
* **When to use**
|
|
79
|
+
*
|
|
80
|
+
* Use to seed atom values from a React component after the current registry
|
|
81
|
+
* already exists.
|
|
82
|
+
*
|
|
83
|
+
* **Gotchas**
|
|
84
|
+
*
|
|
85
|
+
* Each atom is initialized at most once for a given registry by this hook, so
|
|
86
|
+
* later calls for the same atom in that registry are ignored.
|
|
87
|
+
*
|
|
59
88
|
* @category hooks
|
|
89
|
+
* @since 4.0.0
|
|
60
90
|
*/
|
|
61
91
|
export const useAtomInitialValues = (initialValues: Iterable<readonly [Atom.Atom<any>, any]>): void => {
|
|
62
92
|
const registry = React.useContext(RegistryContext)
|
|
@@ -74,18 +104,66 @@ export const useAtomInitialValues = (initialValues: Iterable<readonly [Atom.Atom
|
|
|
74
104
|
}
|
|
75
105
|
|
|
76
106
|
/**
|
|
77
|
-
*
|
|
107
|
+
* Subscribes to an atom in the current React registry and returns its current
|
|
108
|
+
* value, optionally mapped through a selector.
|
|
109
|
+
*
|
|
110
|
+
* **When to use**
|
|
111
|
+
*
|
|
112
|
+
* Use when a React component needs to render from an atom value without also
|
|
113
|
+
* returning a setter.
|
|
114
|
+
*
|
|
115
|
+
* **Details**
|
|
116
|
+
*
|
|
117
|
+
* When a selector is provided, the hook maps the atom before subscribing so the
|
|
118
|
+
* component reads the selected value from the current `RegistryContext`.
|
|
119
|
+
*
|
|
120
|
+
* @see {@link useAtom} for reading and updating a writable atom from one component
|
|
121
|
+
* @see {@link useAtomRef} for reading an `AtomRef` directly
|
|
122
|
+
*
|
|
78
123
|
* @category hooks
|
|
124
|
+
* @since 4.0.0
|
|
79
125
|
*/
|
|
80
126
|
export const useAtomValue: {
|
|
81
127
|
/**
|
|
82
|
-
*
|
|
128
|
+
* Subscribes to an atom in the current React registry and returns its current
|
|
129
|
+
* value, optionally mapped through a selector.
|
|
130
|
+
*
|
|
131
|
+
* **When to use**
|
|
132
|
+
*
|
|
133
|
+
* Use when a React component needs to render from an atom value without also
|
|
134
|
+
* returning a setter.
|
|
135
|
+
*
|
|
136
|
+
* **Details**
|
|
137
|
+
*
|
|
138
|
+
* When a selector is provided, the hook maps the atom before subscribing so the
|
|
139
|
+
* component reads the selected value from the current `RegistryContext`.
|
|
140
|
+
*
|
|
141
|
+
* @see {@link useAtom} for reading and updating a writable atom from one component
|
|
142
|
+
* @see {@link useAtomRef} for reading an `AtomRef` directly
|
|
143
|
+
*
|
|
83
144
|
* @category hooks
|
|
145
|
+
* @since 4.0.0
|
|
84
146
|
*/
|
|
85
147
|
<A>(atom: Atom.Atom<A>): A
|
|
86
148
|
/**
|
|
87
|
-
*
|
|
149
|
+
* Subscribes to an atom in the current React registry and returns its current
|
|
150
|
+
* value, optionally mapped through a selector.
|
|
151
|
+
*
|
|
152
|
+
* **When to use**
|
|
153
|
+
*
|
|
154
|
+
* Use when a React component needs to render from an atom value without also
|
|
155
|
+
* returning a setter.
|
|
156
|
+
*
|
|
157
|
+
* **Details**
|
|
158
|
+
*
|
|
159
|
+
* When a selector is provided, the hook maps the atom before subscribing so the
|
|
160
|
+
* component reads the selected value from the current `RegistryContext`.
|
|
161
|
+
*
|
|
162
|
+
* @see {@link useAtom} for reading and updating a writable atom from one component
|
|
163
|
+
* @see {@link useAtomRef} for reading an `AtomRef` directly
|
|
164
|
+
*
|
|
88
165
|
* @category hooks
|
|
166
|
+
* @since 4.0.0
|
|
89
167
|
*/
|
|
90
168
|
<A, B>(atom: Atom.Atom<A>, f: (_: A) => B): B
|
|
91
169
|
} = <A>(atom: Atom.Atom<A>, f?: (_: A) => A): A => {
|
|
@@ -137,8 +215,25 @@ const flattenExit = <A, E>(exit: Exit.Exit<A, E>): A => {
|
|
|
137
215
|
}
|
|
138
216
|
|
|
139
217
|
/**
|
|
140
|
-
*
|
|
218
|
+
* Mounts an atom in the current React registry for the lifetime of the
|
|
219
|
+
* component.
|
|
220
|
+
*
|
|
221
|
+
* **When to use**
|
|
222
|
+
*
|
|
223
|
+
* Use to keep an atom mounted from a React component without reading, writing,
|
|
224
|
+
* or refreshing it.
|
|
225
|
+
*
|
|
226
|
+
* **Details**
|
|
227
|
+
*
|
|
228
|
+
* The hook uses the current `RegistryContext` and releases the mount through
|
|
229
|
+
* React effect cleanup when the component unmounts or when the registry or atom
|
|
230
|
+
* dependency changes.
|
|
231
|
+
*
|
|
232
|
+
* @see {@link useAtomSet} for mounting a writable atom while returning a setter
|
|
233
|
+
* @see {@link useAtomRefresh} for mounting an atom while returning a refresh callback
|
|
234
|
+
*
|
|
141
235
|
* @category hooks
|
|
236
|
+
* @since 4.0.0
|
|
142
237
|
*/
|
|
143
238
|
export const useAtomMount = <A>(atom: Atom.Atom<A>): void => {
|
|
144
239
|
const registry = React.useContext(RegistryContext)
|
|
@@ -146,8 +241,23 @@ export const useAtomMount = <A>(atom: Atom.Atom<A>): void => {
|
|
|
146
241
|
}
|
|
147
242
|
|
|
148
243
|
/**
|
|
149
|
-
*
|
|
244
|
+
* Mounts a writable atom and returns a setter without subscribing to its value.
|
|
245
|
+
*
|
|
246
|
+
* **When to use**
|
|
247
|
+
*
|
|
248
|
+
* Use when a React component needs to update a writable atom without rendering
|
|
249
|
+
* from that atom's value.
|
|
250
|
+
*
|
|
251
|
+
* **Details**
|
|
252
|
+
*
|
|
253
|
+
* The hook mounts the atom and returns a setter. In value mode the setter
|
|
254
|
+
* accepts a write value or updater function; for `AsyncResult` atoms, `promise`
|
|
255
|
+
* and `promiseExit` modes return a promise for the success value or full `Exit`.
|
|
256
|
+
*
|
|
257
|
+
* @see {@link useAtom} for reading and updating the same writable atom
|
|
258
|
+
*
|
|
150
259
|
* @category hooks
|
|
260
|
+
* @since 4.0.0
|
|
151
261
|
*/
|
|
152
262
|
export const useAtomSet = <
|
|
153
263
|
R,
|
|
@@ -172,8 +282,23 @@ export const useAtomSet = <
|
|
|
172
282
|
}
|
|
173
283
|
|
|
174
284
|
/**
|
|
175
|
-
*
|
|
285
|
+
* Mounts an atom and returns a callback that refreshes it in the current React
|
|
286
|
+
* registry.
|
|
287
|
+
*
|
|
288
|
+
* **When to use**
|
|
289
|
+
*
|
|
290
|
+
* Use to expose a React callback that requests a refresh for an atom without
|
|
291
|
+
* reading or writing its value.
|
|
292
|
+
*
|
|
293
|
+
* **Details**
|
|
294
|
+
*
|
|
295
|
+
* The hook uses the current `RegistryContext`, mounts the atom for the
|
|
296
|
+
* component lifetime, and returns a callback that calls `registry.refresh`.
|
|
297
|
+
*
|
|
298
|
+
* @see {@link useAtomMount} for mounting an atom without returning a refresh callback
|
|
299
|
+
*
|
|
176
300
|
* @category hooks
|
|
301
|
+
* @since 4.0.0
|
|
177
302
|
*/
|
|
178
303
|
export const useAtomRefresh = <A>(atom: Atom.Atom<A>): () => void => {
|
|
179
304
|
const registry = React.useContext(RegistryContext)
|
|
@@ -184,8 +309,19 @@ export const useAtomRefresh = <A>(atom: Atom.Atom<A>): () => void => {
|
|
|
184
309
|
}
|
|
185
310
|
|
|
186
311
|
/**
|
|
187
|
-
*
|
|
312
|
+
* Subscribes to a writable atom and returns its current value together with a
|
|
313
|
+
* setter for updating it.
|
|
314
|
+
*
|
|
315
|
+
* **When to use**
|
|
316
|
+
*
|
|
317
|
+
* Use when a React component needs both to render the current value of a
|
|
318
|
+
* writable atom and update it from the same component.
|
|
319
|
+
*
|
|
320
|
+
* @see {@link useAtomValue} for subscribing to an atom without a setter
|
|
321
|
+
* @see {@link useAtomSet} for updating a writable atom without subscribing to its value
|
|
322
|
+
*
|
|
188
323
|
* @category hooks
|
|
324
|
+
* @since 4.0.0
|
|
189
325
|
*/
|
|
190
326
|
export const useAtom = <R, W, const Mode extends "value" | "promise" | "promiseExit" = never>(
|
|
191
327
|
atom: Atom.Writable<R, W>,
|
|
@@ -251,8 +387,28 @@ function atomResultOrSuspend<A, E>(
|
|
|
251
387
|
}
|
|
252
388
|
|
|
253
389
|
/**
|
|
254
|
-
*
|
|
390
|
+
* Reads an `AsyncResult` atom through React Suspense, suspending while the
|
|
391
|
+
* result is initial or configured as waiting.
|
|
392
|
+
*
|
|
393
|
+
* **When to use**
|
|
394
|
+
*
|
|
395
|
+
* Use when a React component should render only after an `AsyncResult` atom has
|
|
396
|
+
* left its initial state, with loading delegated to a Suspense boundary.
|
|
397
|
+
*
|
|
398
|
+
* **Details**
|
|
399
|
+
*
|
|
400
|
+
* `suspendOnWaiting` defaults to `false`. When `includeFailure` is `true`, a
|
|
401
|
+
* failure result is returned instead of being thrown.
|
|
402
|
+
*
|
|
403
|
+
* **Gotchas**
|
|
404
|
+
*
|
|
405
|
+
* Without `includeFailure`, failure results are thrown with
|
|
406
|
+
* `Cause.squash(result.cause)`, so callers need an error boundary for failures.
|
|
407
|
+
*
|
|
408
|
+
* @see {@link useAtomValue} for reading the raw `AsyncResult` value without Suspense
|
|
409
|
+
*
|
|
255
410
|
* @category hooks
|
|
411
|
+
* @since 4.0.0
|
|
256
412
|
*/
|
|
257
413
|
export const useAtomSuspense = <A, E, const IncludeFailure extends boolean = false>(
|
|
258
414
|
atom: Atom.Atom<AsyncResult.AsyncResult<A, E>>,
|
|
@@ -270,8 +426,24 @@ export const useAtomSuspense = <A, E, const IncludeFailure extends boolean = fal
|
|
|
270
426
|
}
|
|
271
427
|
|
|
272
428
|
/**
|
|
273
|
-
*
|
|
429
|
+
* Subscribes a callback to an atom in the current React registry for the
|
|
430
|
+
* component lifetime.
|
|
431
|
+
*
|
|
432
|
+
* **When to use**
|
|
433
|
+
*
|
|
434
|
+
* Use when a React component needs to run a callback for atom changes without
|
|
435
|
+
* reading the atom value during render.
|
|
436
|
+
*
|
|
437
|
+
* **Details**
|
|
438
|
+
*
|
|
439
|
+
* The subscription is installed in a React effect and cleaned up on unmount or
|
|
440
|
+
* dependency change. When `options.immediate` is enabled, the callback receives
|
|
441
|
+
* the current value when the effect subscribes.
|
|
442
|
+
*
|
|
443
|
+
* @see {@link useAtomValue} for reading an atom value during render instead of running a callback
|
|
444
|
+
*
|
|
274
445
|
* @category hooks
|
|
446
|
+
* @since 4.0.0
|
|
275
447
|
*/
|
|
276
448
|
export const useAtomSubscribe = <A>(
|
|
277
449
|
atom: Atom.Atom<A>,
|
|
@@ -286,8 +458,23 @@ export const useAtomSubscribe = <A>(
|
|
|
286
458
|
}
|
|
287
459
|
|
|
288
460
|
/**
|
|
289
|
-
*
|
|
461
|
+
* Subscribes to an atom ref and returns its latest value.
|
|
462
|
+
*
|
|
463
|
+
* **When to use**
|
|
464
|
+
*
|
|
465
|
+
* Use when a React component should render from an `AtomRef.ReadonlyRef`
|
|
466
|
+
* directly instead of reading an atom through the current registry.
|
|
467
|
+
*
|
|
468
|
+
* **Details**
|
|
469
|
+
*
|
|
470
|
+
* The hook subscribes with `ref.subscribe`, triggers re-renders through React
|
|
471
|
+
* state, and returns the current `ref.value`.
|
|
472
|
+
*
|
|
473
|
+
* @see {@link useAtomValue} for reading an `Atom` from the current registry
|
|
474
|
+
* @see {@link useAtomRefPropValue} for reading a property ref value
|
|
475
|
+
*
|
|
290
476
|
* @category hooks
|
|
477
|
+
* @since 4.0.0
|
|
291
478
|
*/
|
|
292
479
|
export const useAtomRef = <A>(ref: AtomRef.ReadonlyRef<A>): A => {
|
|
293
480
|
const [, setValue] = React.useState(ref.value)
|
|
@@ -296,15 +483,47 @@ export const useAtomRef = <A>(ref: AtomRef.ReadonlyRef<A>): A => {
|
|
|
296
483
|
}
|
|
297
484
|
|
|
298
485
|
/**
|
|
299
|
-
*
|
|
486
|
+
* Returns a memoized atom ref for a property of another atom ref.
|
|
487
|
+
*
|
|
488
|
+
* **When to use**
|
|
489
|
+
*
|
|
490
|
+
* Use to derive an `AtomRef` for one property of an object-shaped atom ref.
|
|
491
|
+
*
|
|
492
|
+
* **Details**
|
|
493
|
+
*
|
|
494
|
+
* The hook memoizes `ref.prop(prop)` for the `[ref, prop]` dependency pair and
|
|
495
|
+
* returns the property ref so callers can read, set, update, or subscribe to
|
|
496
|
+
* that nested property.
|
|
497
|
+
*
|
|
498
|
+
* @see {@link useAtomRef} for subscribing to an atom ref value
|
|
499
|
+
* @see {@link useAtomRefPropValue} for subscribing directly to a property value
|
|
500
|
+
*
|
|
300
501
|
* @category hooks
|
|
502
|
+
* @since 4.0.0
|
|
301
503
|
*/
|
|
302
504
|
export const useAtomRefProp = <A, K extends keyof A>(ref: AtomRef.AtomRef<A>, prop: K): AtomRef.AtomRef<A[K]> =>
|
|
303
505
|
React.useMemo(() => ref.prop(prop), [ref, prop])
|
|
304
506
|
|
|
305
507
|
/**
|
|
306
|
-
*
|
|
508
|
+
* Subscribes to a property ref derived from an atom ref and returns its current
|
|
509
|
+
* value.
|
|
510
|
+
*
|
|
511
|
+
* **When to use**
|
|
512
|
+
*
|
|
513
|
+
* Use when a React component needs only the current value of one property from
|
|
514
|
+
* an object-shaped `AtomRef`.
|
|
515
|
+
*
|
|
516
|
+
* **Details**
|
|
517
|
+
*
|
|
518
|
+
* The hook composes `useAtomRefProp(ref, prop)` with `useAtomRef`, so the
|
|
519
|
+
* property ref is memoized for the `[ref, prop]` pair and then subscribed
|
|
520
|
+
* through `ref.subscribe`.
|
|
521
|
+
*
|
|
522
|
+
* @see {@link useAtomRefProp} for returning the property ref directly
|
|
523
|
+
* @see {@link useAtomRef} for subscribing to a whole atom ref value
|
|
524
|
+
*
|
|
307
525
|
* @category hooks
|
|
526
|
+
* @since 4.0.0
|
|
308
527
|
*/
|
|
309
528
|
export const useAtomRefPropValue = <A, K extends keyof A>(ref: AtomRef.AtomRef<A>, prop: K): A[K] =>
|
|
310
529
|
useAtomRef(useAtomRefProp(ref, prop))
|
package/src/ReactHydration.ts
CHANGED
|
@@ -1,5 +1,26 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* React helpers for hydrating Atom registry state that was serialized on the
|
|
3
|
+
* server or produced by a previous render. This module exposes
|
|
4
|
+
* {@link HydrationBoundary}, a client component that receives dehydrated Atom
|
|
5
|
+
* values and applies them to the nearest {@link RegistryContext} before
|
|
6
|
+
* rendering children when it is safe to do so.
|
|
7
|
+
*
|
|
8
|
+
* **Common use cases**
|
|
9
|
+
*
|
|
10
|
+
* - Reusing Atom values that were collected during server rendering
|
|
11
|
+
* - Restoring client-side Atom state around a routed subtree
|
|
12
|
+
* - Keeping Atom-backed React trees consistent during hydration and transitions
|
|
13
|
+
*
|
|
14
|
+
* **React gotchas**
|
|
15
|
+
*
|
|
16
|
+
* - New Atom values can be hydrated during render so children see them
|
|
17
|
+
* immediately.
|
|
18
|
+
* - Existing Atom values are queued until after commit to avoid updating the
|
|
19
|
+
* current UI with transition data that might later be discarded.
|
|
20
|
+
* - Hydration is idempotent, so repeated or older dehydrated values are safe to
|
|
21
|
+
* pass through the boundary.
|
|
22
|
+
*
|
|
23
|
+
* @since 4.0.0
|
|
3
24
|
*/
|
|
4
25
|
"use client"
|
|
5
26
|
import * as Hydration from "effect/unstable/reactivity/Hydration"
|
|
@@ -7,8 +28,11 @@ import * as React from "react"
|
|
|
7
28
|
import { RegistryContext } from "./RegistryContext.ts"
|
|
8
29
|
|
|
9
30
|
/**
|
|
10
|
-
*
|
|
31
|
+
* Props for a boundary that applies dehydrated Atom values to the nearest
|
|
32
|
+
* {@link RegistryContext} while rendering its children.
|
|
33
|
+
*
|
|
11
34
|
* @category components
|
|
35
|
+
* @since 4.0.0
|
|
12
36
|
*/
|
|
13
37
|
export interface HydrationBoundaryProps {
|
|
14
38
|
state?: Iterable<Hydration.DehydratedAtom>
|
|
@@ -16,8 +40,25 @@ export interface HydrationBoundaryProps {
|
|
|
16
40
|
}
|
|
17
41
|
|
|
18
42
|
/**
|
|
19
|
-
*
|
|
43
|
+
* Hydrates dehydrated Atom values into the current Atom registry for a React
|
|
44
|
+
* subtree.
|
|
45
|
+
*
|
|
46
|
+
* **When to use**
|
|
47
|
+
*
|
|
48
|
+
* Use to apply dehydrated Atom state to a React subtree that reads from the
|
|
49
|
+
* nearest `RegistryContext`.
|
|
50
|
+
*
|
|
51
|
+
* **Details**
|
|
52
|
+
*
|
|
53
|
+
* New Atom values are hydrated during render so descendants can read them
|
|
54
|
+
* immediately, while values for existing Atoms are deferred until after commit
|
|
55
|
+
* so transition data does not update the current UI before React accepts it.
|
|
56
|
+
*
|
|
57
|
+
* @see {@link Hydration.dehydrate} for producing dehydrated Atom state
|
|
58
|
+
* @see {@link Hydration.hydrate} for lower-level non-React hydration
|
|
59
|
+
*
|
|
20
60
|
* @category components
|
|
61
|
+
* @since 4.0.0
|
|
21
62
|
*/
|
|
22
63
|
export const HydrationBoundary: React.FC<HydrationBoundaryProps> = ({
|
|
23
64
|
children,
|
package/src/RegistryContext.ts
CHANGED
|
@@ -1,5 +1,27 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* The `RegistryContext` module provides the React context used by Effect Atom
|
|
3
|
+
* hooks to share an `AtomRegistry` across a component tree. The registry owns
|
|
4
|
+
* atom state, scheduling, and idle cleanup, so components that read or write
|
|
5
|
+
* atoms can coordinate through the same runtime instead of each creating an
|
|
6
|
+
* isolated registry.
|
|
7
|
+
*
|
|
8
|
+
* **Common tasks**
|
|
9
|
+
*
|
|
10
|
+
* - Use {@link RegistryProvider} to scope atom state to a React subtree
|
|
11
|
+
* - Seed atoms for tests, stories, or server-provided data with `initialValues`
|
|
12
|
+
* - Override scheduling or idle timing for custom rendering environments
|
|
13
|
+
* - Read {@link RegistryContext} when integrating lower-level atom APIs
|
|
14
|
+
*
|
|
15
|
+
* **Gotchas**
|
|
16
|
+
*
|
|
17
|
+
* - This is a client module because it depends on React runtime hooks and the
|
|
18
|
+
* scheduler package
|
|
19
|
+
* - A provider keeps the registry stable across renders and disposes it shortly
|
|
20
|
+
* after unmount, allowing React remounts to reuse the same registry
|
|
21
|
+
* - Overriding `scheduleTask` changes when atom work is flushed, so it should
|
|
22
|
+
* return a cancellation function compatible with React unmounts
|
|
23
|
+
*
|
|
24
|
+
* @since 4.0.0
|
|
3
25
|
*/
|
|
4
26
|
"use client"
|
|
5
27
|
|
|
@@ -9,8 +31,11 @@ import * as React from "react"
|
|
|
9
31
|
import * as Scheduler from "scheduler"
|
|
10
32
|
|
|
11
33
|
/**
|
|
12
|
-
*
|
|
34
|
+
* Schedules Atom registry work with React's scheduler at low priority and
|
|
35
|
+
* returns a cancellation function for the scheduled task.
|
|
36
|
+
*
|
|
13
37
|
* @category context
|
|
38
|
+
* @since 4.0.0
|
|
14
39
|
*/
|
|
15
40
|
export function scheduleTask(f: () => void): () => void {
|
|
16
41
|
const node = Scheduler.unstable_scheduleCallback(Scheduler.unstable_LowPriority, f)
|
|
@@ -18,8 +43,20 @@ export function scheduleTask(f: () => void): () => void {
|
|
|
18
43
|
}
|
|
19
44
|
|
|
20
45
|
/**
|
|
21
|
-
*
|
|
46
|
+
* React context that supplies the `AtomRegistry` used by Atom hooks and
|
|
47
|
+
* hydration helpers, defaulting to a standalone registry when no provider is
|
|
48
|
+
* present.
|
|
49
|
+
*
|
|
50
|
+
* **When to use**
|
|
51
|
+
*
|
|
52
|
+
* Use to supply an existing `AtomRegistry` through React context when hooks or
|
|
53
|
+
* hydration helpers need to share registry state that is managed outside
|
|
54
|
+
* `RegistryProvider`.
|
|
55
|
+
*
|
|
56
|
+
* @see {@link RegistryProvider} for creating and providing a registry for a React subtree
|
|
57
|
+
*
|
|
22
58
|
* @category context
|
|
59
|
+
* @since 4.0.0
|
|
23
60
|
*/
|
|
24
61
|
export const RegistryContext = React.createContext<AtomRegistry.AtomRegistry>(AtomRegistry.make({
|
|
25
62
|
scheduleTask,
|
|
@@ -27,8 +64,30 @@ export const RegistryContext = React.createContext<AtomRegistry.AtomRegistry>(At
|
|
|
27
64
|
}))
|
|
28
65
|
|
|
29
66
|
/**
|
|
30
|
-
*
|
|
67
|
+
* Provides a stable `AtomRegistry` to a React subtree, optionally seeding
|
|
68
|
+
* initial atom values and overriding registry scheduling or idle settings.
|
|
69
|
+
*
|
|
70
|
+
* **When to use**
|
|
71
|
+
*
|
|
72
|
+
* Use to scope atom state, scheduling, and idle cleanup to a React subtree.
|
|
73
|
+
*
|
|
74
|
+
* **Details**
|
|
75
|
+
*
|
|
76
|
+
* The provider creates one `AtomRegistry` with `AtomRegistry.make`, passes it
|
|
77
|
+
* through `RegistryContext.Provider`, and forwards `initialValues`,
|
|
78
|
+
* `scheduleTask`, `timeoutResolution`, and `defaultIdleTTL` only when that
|
|
79
|
+
* registry is created.
|
|
80
|
+
*
|
|
81
|
+
* **Gotchas**
|
|
82
|
+
*
|
|
83
|
+
* Option changes after the first render do not rebuild the registry. When the
|
|
84
|
+
* provider unmounts, registry disposal is delayed briefly and canceled if the
|
|
85
|
+
* provider remounts before the timeout fires.
|
|
86
|
+
*
|
|
87
|
+
* @see {@link RegistryContext} for the React context supplied by this provider
|
|
88
|
+
*
|
|
31
89
|
* @category context
|
|
90
|
+
* @since 4.0.0
|
|
32
91
|
*/
|
|
33
92
|
export const RegistryProvider = (options: {
|
|
34
93
|
readonly children?: React.ReactNode | undefined
|
package/src/ScopedAtom.ts
CHANGED
|
@@ -1,5 +1,22 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* The `ScopedAtom` module provides a small React integration for creating atom
|
|
3
|
+
* instances that are scoped to a component subtree. A scoped atom bundles a
|
|
4
|
+
* React provider, context, and `use` accessor so each mounted provider owns its
|
|
5
|
+
* own atom instance instead of sharing a single module-level atom.
|
|
6
|
+
*
|
|
7
|
+
* Use `ScopedAtom` when an atom needs to be isolated per feature, route,
|
|
8
|
+
* component instance, test harness, or provider input. The provider may receive
|
|
9
|
+
* an initial `value` that is passed to the atom factory, making it useful for
|
|
10
|
+
* state that should be seeded from React props while still being consumed by the
|
|
11
|
+
* atom hooks in descendants.
|
|
12
|
+
*
|
|
13
|
+
* **Gotchas**
|
|
14
|
+
*
|
|
15
|
+
* - `use` must be called under the matching provider or it throws.
|
|
16
|
+
* - The provider creates the atom once for its lifetime; changing the provider
|
|
17
|
+
* `value` prop after mount does not recreate the atom.
|
|
18
|
+
*
|
|
19
|
+
* @since 4.0.0
|
|
3
20
|
*/
|
|
4
21
|
"use client"
|
|
5
22
|
|
|
@@ -7,35 +24,42 @@ import type * as Atom from "effect/unstable/reactivity/Atom"
|
|
|
7
24
|
import * as React from "react"
|
|
8
25
|
|
|
9
26
|
/**
|
|
10
|
-
*
|
|
11
|
-
* @category Type IDs
|
|
27
|
+
* Literal type used as the `ScopedAtom` type identifier.
|
|
12
28
|
*
|
|
13
|
-
*
|
|
29
|
+
* **Details**
|
|
30
|
+
*
|
|
31
|
+
* Used as the computed property key and marker value stored on `ScopedAtom`
|
|
32
|
+
* objects.
|
|
33
|
+
*
|
|
34
|
+
* @category type IDs
|
|
35
|
+
* @since 4.0.0
|
|
14
36
|
*/
|
|
15
37
|
export type TypeId = "~@effect/atom-react/ScopedAtom"
|
|
16
38
|
|
|
17
39
|
/**
|
|
18
|
-
* @since 1.0.0
|
|
19
|
-
* @category Type IDs
|
|
20
|
-
*
|
|
21
40
|
* Type identifier for ScopedAtom.
|
|
41
|
+
*
|
|
42
|
+
* **Details**
|
|
43
|
+
*
|
|
44
|
+
* Used as the computed property key and marker value stored on `ScopedAtom`
|
|
45
|
+
* objects.
|
|
46
|
+
*
|
|
47
|
+
* @category type IDs
|
|
48
|
+
* @since 4.0.0
|
|
22
49
|
*/
|
|
23
50
|
export const TypeId: TypeId = "~@effect/atom-react/ScopedAtom"
|
|
24
51
|
|
|
25
52
|
/**
|
|
26
|
-
* @since 1.0.0
|
|
27
|
-
* @category models
|
|
28
|
-
*
|
|
29
53
|
* Scoped Atom interface with a provider-backed instance.
|
|
30
54
|
*
|
|
31
|
-
*
|
|
55
|
+
* **Example** (Providing and reading a scoped atom)
|
|
56
|
+
*
|
|
32
57
|
* ```ts
|
|
33
|
-
* import
|
|
58
|
+
* import { make, useAtomValue } from "@effect/atom-react"
|
|
59
|
+
* import { Atom } from "effect/unstable/reactivity"
|
|
34
60
|
* import * as React from "react"
|
|
35
|
-
* import * as ScopedAtom from "@effect/atom-react/ScopedAtom"
|
|
36
|
-
* import { useAtomValue } from "@effect/atom-react"
|
|
37
61
|
*
|
|
38
|
-
* const Counter =
|
|
62
|
+
* const Counter = make(() => Atom.make(0))
|
|
39
63
|
*
|
|
40
64
|
* function View() {
|
|
41
65
|
* const atom = Counter.use()
|
|
@@ -47,29 +71,45 @@ export const TypeId: TypeId = "~@effect/atom-react/ScopedAtom"
|
|
|
47
71
|
* return React.createElement(Counter.Provider, null, React.createElement(View))
|
|
48
72
|
* }
|
|
49
73
|
* ```
|
|
74
|
+
*
|
|
75
|
+
* @category models
|
|
76
|
+
* @since 4.0.0
|
|
50
77
|
*/
|
|
51
78
|
export interface ScopedAtom<A extends Atom.Atom<any>, Input = never> {
|
|
52
79
|
readonly [TypeId]: TypeId
|
|
53
80
|
use(): A
|
|
54
|
-
Provider: Input extends never ? React.FC<{ readonly children?: React.ReactNode | undefined }>
|
|
81
|
+
Provider: [Input] extends [never] ? React.FC<{ readonly children?: React.ReactNode | undefined }>
|
|
55
82
|
: React.FC<{ readonly children?: React.ReactNode | undefined; readonly value: Input }>
|
|
56
83
|
Context: React.Context<A>
|
|
57
84
|
}
|
|
58
85
|
|
|
59
86
|
/**
|
|
60
|
-
* @since 1.0.0
|
|
61
|
-
* @category constructors
|
|
62
|
-
*
|
|
63
87
|
* Creates a ScopedAtom from a factory function.
|
|
64
88
|
*
|
|
65
|
-
*
|
|
89
|
+
* **When to use**
|
|
90
|
+
*
|
|
91
|
+
* Use to create an atom instance that is owned by a React provider and scoped
|
|
92
|
+
* to a component subtree.
|
|
93
|
+
*
|
|
94
|
+
* **Details**
|
|
95
|
+
*
|
|
96
|
+
* The returned scoped atom includes a `Provider`, `Context`, and `use`
|
|
97
|
+
* accessor. The provider creates the atom once for its lifetime, passing the
|
|
98
|
+
* `value` prop to the factory when the scoped atom expects input.
|
|
99
|
+
*
|
|
100
|
+
* **Gotchas**
|
|
101
|
+
*
|
|
102
|
+
* `use` must run under the matching provider. Changing the provider `value`
|
|
103
|
+
* prop after mount does not recreate the atom.
|
|
104
|
+
*
|
|
105
|
+
* **Example** (Creating a scoped atom with input)
|
|
106
|
+
*
|
|
66
107
|
* ```ts
|
|
67
|
-
* import
|
|
108
|
+
* import { make, useAtomValue } from "@effect/atom-react"
|
|
109
|
+
* import { Atom } from "effect/unstable/reactivity"
|
|
68
110
|
* import * as React from "react"
|
|
69
|
-
* import * as ScopedAtom from "@effect/atom-react/ScopedAtom"
|
|
70
|
-
* import { useAtomValue } from "@effect/atom-react"
|
|
71
111
|
*
|
|
72
|
-
* const User =
|
|
112
|
+
* const User = make((name: string) => Atom.make(name))
|
|
73
113
|
*
|
|
74
114
|
* function UserName() {
|
|
75
115
|
* const atom = User.use()
|
|
@@ -85,6 +125,9 @@ export interface ScopedAtom<A extends Atom.Atom<any>, Input = never> {
|
|
|
85
125
|
* )
|
|
86
126
|
* }
|
|
87
127
|
* ```
|
|
128
|
+
*
|
|
129
|
+
* @category constructors
|
|
130
|
+
* @since 4.0.0
|
|
88
131
|
*/
|
|
89
132
|
export const make = <A extends Atom.Atom<any>, Input = never>(
|
|
90
133
|
f: (() => A) | ((input: Input) => A)
|
package/src/index.ts
CHANGED
|
@@ -1,23 +1,23 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* @since
|
|
2
|
+
* @since 4.0.0
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
|
-
* @since
|
|
6
|
+
* @since 4.0.0
|
|
7
7
|
*/
|
|
8
8
|
export * from "./Hooks.ts"
|
|
9
9
|
|
|
10
10
|
/**
|
|
11
|
-
* @since
|
|
11
|
+
* @since 4.0.0
|
|
12
12
|
*/
|
|
13
13
|
export * from "./RegistryContext.ts"
|
|
14
14
|
|
|
15
15
|
/**
|
|
16
|
-
* @since
|
|
16
|
+
* @since 4.0.0
|
|
17
17
|
*/
|
|
18
18
|
export * from "./ReactHydration.ts"
|
|
19
19
|
|
|
20
20
|
/**
|
|
21
|
-
* @since
|
|
21
|
+
* @since 4.0.0
|
|
22
22
|
*/
|
|
23
23
|
export * from "./ScopedAtom.ts"
|