@systemfsoftware/effect-atom-react 0.5.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,487 @@
1
+ import * as Atom from "@systemfsoftware/effect-atom/Atom";
2
+ import * as AtomRegistry from "@systemfsoftware/effect-atom/Registry";
3
+ import * as AsyncResult from "@systemfsoftware/effect-atom/Result";
4
+ import * as Effect from "effect/Effect";
5
+ import * as React from "react";
6
+ import * as Hydration from "@systemfsoftware/effect-atom/Hydration";
7
+ import * as AtomRef from "@systemfsoftware/effect-atom/AtomRef";
8
+ //#region src/Hooks.d.ts
9
+ /**
10
+ * Seeds initial atom values in the current React atom registry.
11
+ *
12
+ * **When to use**
13
+ *
14
+ * Use to seed atom values from a React component after the current registry
15
+ * already exists.
16
+ *
17
+ * **Gotchas**
18
+ *
19
+ * Each atom is initialized at most once for a given registry by this hook, so
20
+ * later calls for the same atom in that registry are ignored.
21
+ *
22
+ * @category hooks
23
+ * @since 4.0.0
24
+ */
25
+ declare const useAtomInitialValues: (initialValues: Iterable<readonly [Atom.Atom<unknown>, unknown]>) => void;
26
+ /**
27
+ * Subscribes to an atom in the current React registry and returns its current
28
+ * value, optionally mapped through a selector.
29
+ *
30
+ * **When to use**
31
+ *
32
+ * Use when a React component needs to render from an atom value without also
33
+ * returning a setter.
34
+ *
35
+ * **Details**
36
+ *
37
+ * When a selector is provided, the hook maps the atom before subscribing so the
38
+ * component reads the selected value from the current `RegistryContext`.
39
+ *
40
+ * @see {@link useAtom} for reading and updating a writable atom from one component
41
+ * @see {@link useAtomRef} for reading an `AtomRef` directly
42
+ *
43
+ * @category hooks
44
+ * @since 4.0.0
45
+ */
46
+ declare const useAtomValue: {
47
+ <A>(atom: Atom.Atom<A>): A;
48
+ <A, B>(atom: Atom.Atom<A>, f: (_: A) => B): B;
49
+ };
50
+ /**
51
+ * Mounts an atom in the current React registry for the lifetime of the
52
+ * component.
53
+ *
54
+ * **When to use**
55
+ *
56
+ * Use to keep an atom mounted from a React component without reading, writing,
57
+ * or refreshing it.
58
+ *
59
+ * **Details**
60
+ *
61
+ * The hook uses the current `RegistryContext` and releases the mount through
62
+ * React effect cleanup when the component unmounts or when the registry or atom
63
+ * dependency changes.
64
+ *
65
+ * @see {@link useAtomSet} for mounting a writable atom while returning a setter
66
+ * @see {@link useAtomRefresh} for mounting an atom while returning a refresh callback
67
+ *
68
+ * @category hooks
69
+ * @since 4.0.0
70
+ */
71
+ declare const useAtomMount: <A>(atom: Atom.Atom<A>) => void;
72
+ /**
73
+ * Mounts a writable atom and returns a setter without subscribing to its value.
74
+ *
75
+ * **When to use**
76
+ *
77
+ * Use when a React component needs to update a writable atom without rendering
78
+ * from that atom's value.
79
+ *
80
+ * The hook mounts the atom and returns a setter that writes a complete value.
81
+ * For updates computed from the current value use `useAtomUpdate`.
82
+ *
83
+ * @see {@link useAtom} for reading and updating the same writable atom
84
+ * @see {@link useAtomSetResult} for a setter that resolves once the write settles
85
+ * @see {@link useAtomUpdate} for a setter that applies an updater function
86
+ *
87
+ * @category hooks
88
+ * @since 4.0.0
89
+ */
90
+ declare const useAtomSet: <R, W>(atom: Atom.Writable<R, W>) => (value: W) => void;
91
+ /**
92
+ * Mounts a writable `AsyncResult` atom and returns a setter whose returned
93
+ * effect resolves to the settled success value.
94
+ *
95
+ * **When to use**
96
+ *
97
+ * Use when a component writes to an `AsyncResult` atom and needs to know when
98
+ * the write has been applied, so a save button can show a confirming state or
99
+ * report a failure.
100
+ *
101
+ * The hook mounts the atom and returns a setter that writes a new value and
102
+ * returns the effect of the atom leaving its initial state, failing with the
103
+ * write result's cause when the write fails.
104
+ *
105
+ * @see {@link useAtomSet} for writing without waiting for settlement
106
+ *
107
+ * @category hooks
108
+ * @since 4.0.0
109
+ */
110
+ declare const useAtomSetResult: <A, E, W>(atom: Atom.Writable<AsyncResult.Result<A, E>, W>) => (value: W) => Effect.Effect<A, E>;
111
+ /**
112
+ * Mounts a writable atom and returns an updater that applies a function to the
113
+ * current value.
114
+ *
115
+ * **When to use**
116
+ *
117
+ * Use when a component needs to update a writable atom from its current value,
118
+ * such as incrementing a counter, without subscribing to the atom.
119
+ *
120
+ * @see {@link useAtomSet} for writing a complete value
121
+ *
122
+ * @category hooks
123
+ * @since 4.0.0
124
+ */
125
+ declare const useAtomUpdate: <R, W>(atom: Atom.Writable<R, W>) => (f: (previous: R) => W) => void;
126
+ /**
127
+ * Mounts an atom and returns a callback that refreshes it in the current React
128
+ * registry.
129
+ *
130
+ * **When to use**
131
+ *
132
+ * Use to expose a React callback that requests a refresh for an atom without
133
+ * reading or writing its value.
134
+ *
135
+ * **Details**
136
+ *
137
+ * The hook uses the current `RegistryContext`, mounts the atom for the
138
+ * component lifetime, and returns a callback that calls `registry.refresh`.
139
+ *
140
+ * @see {@link useAtomMount} for mounting an atom without returning a refresh callback
141
+ *
142
+ * @category hooks
143
+ * @since 4.0.0
144
+ */
145
+ declare const useAtomRefresh: <A>(atom: Atom.Atom<A>) => () => void;
146
+ /**
147
+ * Subscribes to a writable atom and returns its current value together with a
148
+ * setter for updating it.
149
+ *
150
+ * **When to use**
151
+ *
152
+ * Use when a React component needs both to render the current value of a
153
+ * writable atom and update it from the same component.
154
+ *
155
+ * @see {@link useAtomValue} for subscribing to an atom without a setter
156
+ * @see {@link useAtomSet} for updating a writable atom without subscribing to its value
157
+ *
158
+ * @category hooks
159
+ * @since 4.0.0
160
+ */
161
+ declare const useAtom: <R, W>(atom: Atom.Writable<R, W>) => readonly [value: R, write: (value: W) => void];
162
+ /**
163
+ * Reads an `AsyncResult` atom through React Suspense, suspending while the
164
+ * result is initial or configured as waiting.
165
+ *
166
+ * **When to use**
167
+ *
168
+ * Use when a React component should render only after an `AsyncResult` atom has
169
+ * left its initial state, with loading delegated to a Suspense boundary.
170
+ *
171
+ * **Details**
172
+ *
173
+ * `suspendOnWaiting` defaults to `false`. When `includeFailure` is `true`, a
174
+ * failure result is returned instead of being thrown.
175
+ *
176
+ * **Gotchas**
177
+ *
178
+ * Without `includeFailure`, failure results are thrown with
179
+ * `Cause.squash(result.cause)`, so callers need an error boundary for failures.
180
+ *
181
+ * @see {@link useAtomValue} for reading the raw `AsyncResult` value without Suspense
182
+ *
183
+ * @category hooks
184
+ * @since 4.0.0
185
+ */
186
+ declare const useAtomSuspense: <A, E>(atom: Atom.Atom<AsyncResult.Result<A, E>>, options?: {
187
+ readonly suspendOnWaiting?: boolean | undefined;
188
+ readonly includeFailure?: boolean | undefined;
189
+ }) => AsyncResult.Success<A, E> | AsyncResult.Failure<A, E>;
190
+ /**
191
+ * Subscribes a callback to an atom in the current React registry for the
192
+ * component lifetime.
193
+ *
194
+ * **When to use**
195
+ *
196
+ * Use when a React component needs to run a callback for atom changes without
197
+ * reading the atom value during render.
198
+ *
199
+ * **Details**
200
+ *
201
+ * The subscription is installed in a React effect and cleaned up on unmount or
202
+ * dependency change. When `options.immediate` is enabled, the callback receives
203
+ * the current value when the effect subscribes.
204
+ *
205
+ * @see {@link useAtomValue} for reading an atom value during render instead of running a callback
206
+ *
207
+ * @category hooks
208
+ * @since 4.0.0
209
+ */
210
+ declare const useAtomSubscribe: <A>(atom: Atom.Atom<A>, f: (_: A) => void, options?: {
211
+ readonly immediate?: boolean;
212
+ }) => void;
213
+ /**
214
+ * Subscribes to an atom ref and returns its latest value.
215
+ *
216
+ * **When to use**
217
+ *
218
+ * Use when a React component should render from an `AtomRef.ReadonlyRef`
219
+ * directly instead of reading an atom through the current registry.
220
+ *
221
+ * **Details**
222
+ *
223
+ * The hook subscribes with `ref.subscribe`, triggers re-renders through React
224
+ * state, and returns the current `ref.value`.
225
+ *
226
+ * @see {@link useAtomValue} for reading an `Atom` from the current registry
227
+ * @see {@link useAtomRefPropValue} for reading a property ref value
228
+ *
229
+ * @category hooks
230
+ * @since 4.0.0
231
+ */
232
+ declare const useAtomRef: <A>(ref: AtomRef.ReadonlyRef<A>) => A;
233
+ /**
234
+ * Returns a memoized atom ref for a property of another atom ref.
235
+ *
236
+ * **When to use**
237
+ *
238
+ * Use to derive an `AtomRef` for one property of an object-shaped atom ref.
239
+ *
240
+ * **Details**
241
+ *
242
+ * The hook memoizes `ref.prop(prop)` for the `[ref, prop]` dependency pair and
243
+ * returns the property ref so callers can read, set, update, or subscribe to
244
+ * that nested property.
245
+ *
246
+ * @see {@link useAtomRef} for subscribing to an atom ref value
247
+ * @see {@link useAtomRefPropValue} for subscribing directly to a property value
248
+ *
249
+ * @category hooks
250
+ * @since 4.0.0
251
+ */
252
+ declare const useAtomRefProp: <A, K extends keyof A>(ref: AtomRef.AtomRef<A>, prop: K) => AtomRef.AtomRef<A[K]>;
253
+ /**
254
+ * Subscribes to a property ref derived from an atom ref and returns its current
255
+ * value.
256
+ *
257
+ * **When to use**
258
+ *
259
+ * Use when a React component needs only the current value of one property from
260
+ * an object-shaped `AtomRef`.
261
+ *
262
+ * **Details**
263
+ *
264
+ * The hook composes `useAtomRefProp(ref, prop)` with `useAtomRef`, so the
265
+ * property ref is memoized for the `[ref, prop]` pair and then subscribed
266
+ * through `ref.subscribe`.
267
+ *
268
+ * @see {@link useAtomRefProp} for returning the property ref directly
269
+ * @see {@link useAtomRef} for subscribing to a whole atom ref value
270
+ *
271
+ * @category hooks
272
+ * @since 4.0.0
273
+ */
274
+ declare const useAtomRefPropValue: <A, K extends keyof A>(ref: AtomRef.AtomRef<A>, prop: K) => A[K];
275
+ //#endregion
276
+ //#region src/RegistryContext.d.ts
277
+ /**
278
+ * Schedules Atom registry work with React's scheduler at low priority and
279
+ * returns a cancellation function for the scheduled task.
280
+ *
281
+ * @category context
282
+ * @since 4.0.0
283
+ */
284
+ declare function scheduleTask(f: () => void): () => void;
285
+ /**
286
+ * Provides a React context that supplies the `AtomRegistry` used by Atom hooks and
287
+ * hydration helpers, defaulting to a standalone registry when no provider is
288
+ * present.
289
+ *
290
+ * **When to use**
291
+ *
292
+ * Use to supply an existing `AtomRegistry` through React context when hooks or
293
+ * hydration helpers need to share registry state that is managed outside
294
+ * `RegistryProvider`.
295
+ *
296
+ * @see {@link RegistryProvider} for creating and providing a registry for a React subtree
297
+ *
298
+ * @category context
299
+ * @since 4.0.0
300
+ */
301
+ declare const RegistryContext: React.Context<AtomRegistry.Registry>;
302
+ /**
303
+ * Provides a stable `AtomRegistry` to a React subtree, optionally seeding
304
+ * initial atom values and overriding registry scheduling or idle settings.
305
+ *
306
+ * **When to use**
307
+ *
308
+ * Use to scope atom state, scheduling, and idle cleanup to a React subtree.
309
+ *
310
+ * **Details**
311
+ *
312
+ * The provider creates one `AtomRegistry` with `AtomRegistry.make`, passes it
313
+ * through `RegistryContext.Provider`, and forwards `initialValues`,
314
+ * `scheduleTask`, `timeoutResolution`, and `defaultIdleTTL` only when that
315
+ * registry is created.
316
+ *
317
+ * **Gotchas**
318
+ *
319
+ * Option changes after the first render do not rebuild the registry. When the
320
+ * provider unmounts, registry disposal is delayed briefly and canceled if the
321
+ * provider remounts before the timeout fires.
322
+ *
323
+ * @see {@link RegistryContext} for the React context supplied by this provider
324
+ *
325
+ * @category context
326
+ * @since 4.0.0
327
+ */
328
+ declare const RegistryProvider: (options: {
329
+ readonly children?: React.ReactNode | undefined;
330
+ readonly initialValues?: Iterable<readonly [Atom.Atom<unknown>, unknown]> | undefined;
331
+ readonly scheduleTask?: ((f: () => void) => () => void) | undefined;
332
+ readonly timeoutResolution?: number | undefined;
333
+ readonly defaultIdleTTL?: number | undefined;
334
+ }) => React.FunctionComponentElement<React.ProviderProps<AtomRegistry.Registry>>;
335
+ //#endregion
336
+ //#region src/ReactHydration.d.ts
337
+ /**
338
+ * Props for a boundary that applies dehydrated Atom values to the nearest
339
+ * {@link RegistryContext} while rendering its children.
340
+ *
341
+ * @category components
342
+ * @since 4.0.0
343
+ */
344
+ interface HydrationBoundaryProps {
345
+ state?: Iterable<Hydration.DehydratedAtomValue>;
346
+ children?: React.ReactNode;
347
+ }
348
+ /**
349
+ * Provides a React hydration boundary that loads dehydrated Atom values into
350
+ * the current Atom registry.
351
+ *
352
+ * **When to use**
353
+ *
354
+ * Use to apply dehydrated Atom state to a React subtree that reads from the
355
+ * nearest `RegistryContext`.
356
+ *
357
+ * **Details**
358
+ *
359
+ * New Atom values are hydrated during render so descendants can read them
360
+ * immediately, while values for existing Atoms are deferred until after commit
361
+ * so transition data does not update the current UI before React accepts it.
362
+ *
363
+ * @see {@link Hydration.dehydrate} for producing dehydrated Atom state
364
+ * @see {@link Hydration.hydrate} for lower-level non-React hydration
365
+ *
366
+ * @category components
367
+ * @since 4.0.0
368
+ */
369
+ declare const HydrationBoundary: React.FC<HydrationBoundaryProps>;
370
+ //#endregion
371
+ //#region src/ScopedAtom.d.ts
372
+ /**
373
+ * Literal type used as the `ScopedAtom` type identifier.
374
+ *
375
+ * **Details**
376
+ *
377
+ * Used as the computed property key and marker value stored on `ScopedAtom`
378
+ * objects.
379
+ *
380
+ * @category type IDs
381
+ * @since 4.0.0
382
+ */
383
+ type TypeId = '~@effect/atom-react/ScopedAtom';
384
+ /**
385
+ * Type identifier for ScopedAtom.
386
+ *
387
+ * **Details**
388
+ *
389
+ * Used as the computed property key and marker value stored on `ScopedAtom`
390
+ * objects.
391
+ *
392
+ * @category type IDs
393
+ * @since 4.0.0
394
+ */
395
+ declare const TypeId: TypeId;
396
+ /**
397
+ * Scoped Atom interface with a provider-backed instance.
398
+ *
399
+ * **Example** (Providing and reading a scoped atom)
400
+ *
401
+ * ```ts import.meta.vitest
402
+ * import { make, useAtomValue } from "@effect/atom-react"
403
+ * import { Atom } from "effect/unstable/reactivity"
404
+ * import * as React from "react"
405
+ * import { renderToStaticMarkup } from "react-dom/server"
406
+ *
407
+ * const Counter = make(() => Atom.make(0))
408
+ *
409
+ * function View() {
410
+ * const atom = Counter.use()
411
+ * const value = useAtomValue(atom)
412
+ * return React.createElement("div", null, value)
413
+ * }
414
+ *
415
+ * export function App() {
416
+ * return React.createElement(Counter.Provider, null, React.createElement(View))
417
+ * }
418
+ *
419
+ * renderToStaticMarkup(React.createElement(App)) // => "<div>0</div>"
420
+ * ```
421
+ *
422
+ * @category models
423
+ * @since 4.0.0
424
+ */
425
+ interface ScopedAtom<A extends Atom.Atom<unknown>, Input = never> {
426
+ readonly [TypeId]: TypeId;
427
+ use(): A;
428
+ Provider: [Input] extends [never] ? React.FC<{
429
+ readonly children?: React.ReactNode | undefined;
430
+ }> : React.FC<{
431
+ readonly children?: React.ReactNode | undefined;
432
+ readonly value: Input;
433
+ }>;
434
+ Context: React.Context<A | undefined>;
435
+ }
436
+ /**
437
+ * Creates a ScopedAtom from a factory function.
438
+ *
439
+ * **When to use**
440
+ *
441
+ * Use to create an atom instance that is owned by a React provider and scoped
442
+ * to a component subtree.
443
+ *
444
+ * **Details**
445
+ *
446
+ * The returned scoped atom includes a `Provider`, `Context`, and `use`
447
+ * accessor. The provider creates the atom once for its lifetime, passing the
448
+ * `value` prop to the factory when the scoped atom expects input.
449
+ *
450
+ * **Gotchas**
451
+ *
452
+ * `use` must run under the matching provider. Changing the provider `value`
453
+ * prop after mount does not recreate the atom.
454
+ *
455
+ * **Example** (Creating a scoped atom with input)
456
+ *
457
+ * ```ts import.meta.vitest
458
+ * import { make, useAtomValue } from "@effect/atom-react"
459
+ * import { Atom } from "effect/unstable/reactivity"
460
+ * import * as React from "react"
461
+ * import { renderToStaticMarkup } from "react-dom/server"
462
+ *
463
+ * const User = make((name: string) => Atom.make(name))
464
+ *
465
+ * function UserName() {
466
+ * const atom = User.use()
467
+ * const value = useAtomValue(atom)
468
+ * return React.createElement("span", null, value)
469
+ * }
470
+ *
471
+ * export function App() {
472
+ * return React.createElement(
473
+ * User.Provider,
474
+ * { value: "Ada" },
475
+ * React.createElement(UserName)
476
+ * )
477
+ * }
478
+ *
479
+ * renderToStaticMarkup(React.createElement(App)) // => "<span>Ada</span>"
480
+ * ```
481
+ *
482
+ * @category constructors
483
+ * @since 4.0.0
484
+ */
485
+ declare const make: <A extends Atom.Atom<unknown>, Input = never>(f: (() => A) | ((input: Input) => A)) => ScopedAtom<A, Input>;
486
+ //#endregion
487
+ export { HydrationBoundary, HydrationBoundaryProps, RegistryContext, RegistryProvider, ScopedAtom, TypeId, make, scheduleTask, useAtom, useAtomInitialValues, useAtomMount, useAtomRef, useAtomRefProp, useAtomRefPropValue, useAtomRefresh, useAtomSet, useAtomSetResult, useAtomSubscribe, useAtomSuspense, useAtomUpdate, useAtomValue };