24 KiB
I have the effect v4 source code cloned in ~/YeeBois/projects/beep-effect/.repos/effect-v4 and the important docs and modules are here:
@effect/atom-reacteffect/unstable/reactivity@effect/atom-react documentationeffect/unstable/reactivity documentation
Also I have some other repositories & beep-effect modules we can reference for guidance:
effect/unstable/reactivity/AsyncResultusage ~/YeeBois/dev/hazel/apps/web/src/lib/auth.tsx#L1-41effect/unstable/reactivity/Atomusage ~/YeeBois/dev/hazel/apps/web/src/atoms/tauri-update-atoms.ts#L1-241Atom.runtimeusage (IMPORTANT! don't just use Effect.runPromise, Then we can get telemetry on the browser) /home/elpresidank/YeeBois/dev/ghui/src/services/runtime.ts#L1-62runtime.fnusage /home/elpresidank/YeeBois/dev/ghui/src/ui/pullRequests/atoms.ts/src/ui/pullRequests/atoms.ts#L199-225- Atom.family usage /home/elpresidank/YeeBois/dev/ghui/src/ui/pullRequests/atoms.ts/src/ui/pullRequests/atoms.ts#L180-197
effect/unstable/reactivity/AtomHttpApiusage ~/YeeBois/projects/dev/hazel/apps/web/src/lib/services/common/link-preview-client.ts#L1-10effect/unstable/reactivity/AtomRefusage /home/elpresidank/YeeBois/dev/view-server-smart/packages/client/src/live-client.ts#L1-47effect/unstable/reactivity/AtomRegistryusage /home/elpresidank/YeeBois/dev/ghui/src/App.tsx#L1195-1209effect/unstable/reactivity/AtomRpcusage /home/elpresidank/YeeBois/dev/hazel/apps/web/src/lib/services/common/rpc-atom-client.ts#L1-77effect/unstable/reactivity/Hydrationusage7b85bdcd8f/apps/web/src/lib/atom-utils.tseffect/unstable/reactivity/Reactivityusage01f7a858b7/apps/server/src/persistence/NodeSqliteClient.ts@effect/atom-reactRegistryProvider usage39e9750072/apps/cli/src/program.tsx@effect/atom-reactRegistryContext usage @.context/effect-atom/docs/atom-react/RegistryContext.ts.md#L1-58@effect/atom-reactuseAtomInitialValues usage ~/YeeBois/projects/beep-effect/.repos/effect-v4/packages/atom/solid/test/index.test.tsx#L87-100@effect/atom-reactuseAtomValue usage ~/YeeBois/projects/dev/hazel/apps/web/src/lib/auth.tsx#L28@effect/atom-reactuseAtomMount usage272b18d90f/web/src/App.tsx@effect/atom-reactuseAtom usage272b18d90f/src/App.tsx@effect/atom-reactuseAtomSet usage45dd81c272/apps/web/src/components/theme-provider.tsx@effect/atom-reactuseAtomRefresh usage ~/YeeBois/projects/dev/hazel/apps/web/src/components/profile/profile-picture-upload.tsx#L1-244@effect/atom-reactuseAtomSuspense usage008ad0bf5b/src/App.tsx@effect/atom-reactuseAtomSubscribe usageb3e8c0334b/apps/web/src/rpc/serverState.ts@effect/atom-reactuseAtomRef usage1c23188e01/src/renderer/state/block-lookup-store.ts@effect/atom-reactuseAtomRefProp usagec71ec342dd/apps/web/src/lib/effect-atom.ts@effect/atom-reactuseAtomRefPropValue usagec71ec342dd/apps/web/src/lib/effect-atom.ts@effect/atom-reactReactHydration.HydrationBoundary usage7b85bdcd8f/apps/web/src/routes/todos/index.tsx (L4)@effect/atom-reactScopedAtom usagecdd59bf2cf/.repos/effect-v4/packages/atom/react/src/ScopedAtom.ts
Notes on modules
effect/unstable/reactivity/AsyncResult
State containers for asynchronous values used by the reactivity APIs.AsyncResult records the latest observable state of work that may still beloading, refreshing, retrying, or recovering from failure. The value is one ofInitial, Success, or Failure, and every variant also carries a waitingflag so callers can keep rendering the current state while newer work is inflight.Mental modelThe variant answers "what do we know right now?", while waiting answers "isnewer work currently running?". A success contains the current value and itstimestamp. A failure contains a Cause and may also keep the previoussuccess, which lets UI and atom code show stale data while exposing the latestfailure for error displays and retry logic.Common tasks- Start with {@link initial}, {@link success}, {@link failure}, or {@link fail}- Convert Effect exits with {@link fromExit} and {@link fromExitWithPrevious}- Mark existing state as loading with {@link waiting} or {@link waitingFrom}- Read values and failures with {@link value}, {@link cause}, {@link error}, {@link getOrElse}, and {@link toExit}- Transform and combine results with {@link map}, {@link flatMap}, and {@link all}- Render all states with {@link match}, {@link matchWithWaiting}, or {@link builder}Gotchas- waiting is an overlay, not a fourth variant; any variant can be waiting.- {@link value} and {@link getOrElse} can read the previous success stored in a failure, so inspect {@link cause} or {@link error} when stale data and a current success must be distinguished.- {@link matchWithWaiting} handles waiting before variant-specific branches, while {@link match} and {@link matchWithError} expose the underlying variant first.
effect/unstable/reactivity/Atom
Reactive state primitives for values evaluated by an {@link AtomRegistry}.An {@link Atom} describes how to read a value. The registry is the runtimeowner: it evaluates reads, caches results, records dependency edges, runseffects and streams with the configured runtime services, and disposes nodeswhen they are no longer observed.Mental modelRegular get(atom) calls inside a read function create dependencies. When adependency changes or refreshes, dependent atoms are invalidated and re-readon demand. One-shot reads such as get.once(atom) read the current valuewithout creating an edge. The same atom can hold different cached values indifferent registries, so stable atom identity matters; use {@link family} foratoms parameterized by input values.Common tasksUse {@link readable} or {@link writable} for synchronous state, {@link make}for effects and streams exposed as AsyncResult, {@link fn} forcommand-style effects, {@link pull} for pull-based streams, and{@link subscriptionRef} to expose a SubscriptionRef. Use {@link kvs},{@link searchParam}, and {@link serializable} when atom values needpersistence, URL state, or server-to-client hydration. Read and mutate atomsfrom Effect code with {@link get}, {@link set}, {@link update},{@link refresh}, and {@link mount}; convert observed values to streams with{@link toStream} or {@link toStreamResult}.GotchasCache lifetime belongs to the registry, not the atom object. Unobservednon-keepAlive atoms can be disposed immediately or after their idle TTL,which also releases finalizers and may rebuild effects, streams, and derivedstate on the next read. Runtime-backed atoms refresh only through theirregistered refresh hooks or explicit Reactivity invalidations; reading anEffect by itself does not keep external data subscribed.
effect/unstable/reactivity/AtomHttpApi
The AtomHttpApi module adapts typed HttpApi clients to the unstable atomreactivity runtime. Use it to define a Context.Service whose generated HTTPAPI client is available directly and whose endpoints can also be invoked asatoms: query creates an atom of AsyncResult for reads, while mutationcreates an AtomResultFn for writes.It is intended for applications that want server state to participate in atomcaching, invalidation, and hydration. Queries can be associated withreactivityKeys so they refresh when those keys are invalidated, mutations caninvalidate the same keys after the request succeeds, and timeToLive controlswhether idle query atoms expire, stay alive for a duration, or are kept alive.Serialization is schema-based and intentionally limited to decoded values.Mutation atoms are serializable only in "decoded-only" mode, while queryatoms are serializable only in "decoded-only" mode when a stableserializationKey is supplied. Choose serialization keys that uniquelyidentify the endpoint request, keep reactivity keys stable across client andserver registries during hydration, and avoid serializing response modes thatexpose raw HttpClientResponse values.The service wraps HttpApiClient.make, so the same HttpApi definition,schemas, base URL, middleware services, and HTTP client layer must be availablewherever the atom runtime is constructed. Use transformClient andtransformResponse for cross-cutting client behavior, and remember thatschema or low-level HTTP client failures are raised as defects while endpointand middleware failures remain typed errors.
effect/unstable/reactivity/AtomRef
Mutable reactive references for local, in-memory state.AtomRef provides small observable state cells that can be read, updated,focused, and subscribed to without going through an AtomRegistry. It issuited to form state, local view models, and collections of item referenceswhere callers need direct mutation methods together with change notifications.Mental modelAn AtomRef is a value cell with a stable key and a subscriber list.{@link make} creates a mutable cell, map derives read-only views, and propfocuses on nested object or array properties while preserving mutationhelpers. {@link collection} stores ordered item refs and emits collectionupdates when items are inserted, removed, or changed through their refs.Common tasks- Use {@link make} for standalone local state.- Use ref.set or ref.update to replace the current value.- Use ref.map for derived read-only values.- Use ref.prop to update nested object fields or array entries.- Use {@link collection} for ordered lists whose items should remain individually mutable.GotchasNotifications are equality-aware: values that are Equal.equals to thecurrent value do not notify subscribers, and mapped or property subscriptionsonly emit when their derived value changes. Directly mutating an object orarray stored in a ref does not notify listeners; use set, update, or aproperty ref instead. toArray returns the current raw item values, not theitem refs.
effect/unstable/reactivity/AtomRegistry
The AtomRegistry module provides the runtime cache for unstable reactivityatoms. A registry owns the node graph for a group of atoms, stores currentvalues, records parent and child dependencies while atoms are read, andcoordinates writes, refreshes, subscriptions, stream conversions, and nodedisposal.Use a separate registry for each UI root, request, test, route boundary, orother lifetime that needs isolated atom state. The same atom object can havedifferent cached values in different registries, while serializable atoms usetheir stable serialization key so preloaded values can hydrate a node beforethe first read.Mental model- Reading an atom creates or reuses a registry node, evaluates the atom when its value is missing or stale, and records any nested atom reads as dependencies.- Writing a writable atom updates its node through the atom's write function, invalidates dependent nodes, and notifies listeners after batching settles.- Subscriptions and scoped {@link mount} calls keep nodes alive. When the last listener and dependent child disappear, non-keepAlive atoms can be removed immediately or after their idle TTL.- Effects and streams started by atoms run with the registry scheduler and are finalized when the node is rebuilt, removed, reset, or disposed.- Disposing a registry clears its nodes and makes later atom access an error.Common tasks- Create a registry directly with {@link make}, or provide one to Effect programs with {@link layer} or {@link layerOptions}.- Read and write atom state with the registry instance methods get, set, modify, update, and refresh.- Keep an atom alive for an Effect scope with {@link mount}; subscribe with subscribe when integrating with callback-based UI code.- Convert observed atom values with {@link toStream} and {@link toStreamResult}, or wait for an AsyncResult atom with {@link getResult}.- Preload encoded serializable state with setSerializable before the matching atom is read.Quickstart****Example (Isolated atom state)tsimport { Atom, AtomRegistry } from "effect/unstable/reactivity"const count = Atom.make(0)const doubled = Atom.make((get) => get(count) * 2)const registry = AtomRegistry.make()registry.set(count, 21)registry.get(doubled)// 42Gotchas- Atom identity matters. Creating a new atom object creates a different node unless the atom is serializable and uses the same serialization key.- Unobserved atoms without keepAlive can be removed, so later reads may rebuild derived values, restart effects or streams, and rerun finalizers.- subscribe and the instance mount method return release callbacks; call them when the external consumer is done. The exported {@link mount} helper ties that release to an Effect scope.- reset and dispose remove every node in the registry. Use a new registry when a whole lifetime should start from empty state.See also
effect/unstable/reactivity/AtomRpc
The AtomRpc module connects typed RPC clients to the atom reactivity
runtime. It builds a Context.Service that exposes the flattened
RpcClient, an AtomRuntime, mutation helpers, and query helpers for every
RPC in an RpcGroup.
Use it when remote read models should be represented as atoms, mutations
should refresh affected reads through Reactivity keys, or non-streaming
query results need serialization metadata for hydration. The RPC protocol
layer supplies the transport, and may be static or derived from the current
atom context, so request headers, transport dependencies, and client
middleware remain part of the normal Effect environment.
Non-streaming queries produce atoms of AsyncResult values. Supplying a
serializationKey marks those query atoms as serializable using codecs
derived from the RPC success schema and the combined RPC, middleware, and
client error schemas; choose stable, unique keys when dehydrating. Streaming
RPCs produce writable pull atoms instead, so callers advance the stream by
writing to the atom and should not expect serialization metadata. Query family
caching includes the payload, normalized headers, reactivity keys, TTL, and
serialization key, so use stable values for those inputs when atom identity
matters.
effect/unstable/reactivity/Hydration
Moves serializable reactivity state between AtomRegistry instances.
The Hydration module snapshots atoms marked with Atom.serializable and
loads those encoded values into another registry before the atoms are read. It
is useful for server rendering, browser bootstrapping, route transitions, and
other handoffs where a new registry should start from values computed by a
previous one.
Mental model
dehydrate walks the source registry and produces DehydratedAtomValue
records keyed by each atom's serialization key. hydrate stores those encoded
values in the target registry so the matching atom can decode them with its own
serializable codec. Atom identity is not transferred; only the stable key,
encoded value, dehydration timestamp, and optional async handoff are.
Common tasks
Use dehydrate before rendering or crossing a route boundary, then call
hydrate on the registry that will serve the next read. Use toValues when
code accepts generic DehydratedAtom entries but needs the concrete record
shape, and use encodeInitialAs to choose how AsyncResult.Initial values
should appear in the snapshot.
Gotchas
Only serializable atoms are included, and the target registry must contain
atoms with matching stable keys and compatible codecs. The optional
resultPromise used for AsyncResult.Initial is a live JavaScript promise; it
can be handed to another registry in the same runtime, but it cannot be sent
through JSON or across process boundaries.
effect/unstable/reactivity/Reactivity
The Reactivity module provides process-local invalidation for connectingwrites to dependent reads. It does not cache values itself; it tracks keys,registers query handlers, and reruns effects when matching keys areinvalidated so queues, streams, UI subscriptions, and read models can stayfresh after successful writes.Mental modelA query registers one or more keys, runs once immediately, and publishes eachresult to a queue or stream. Invalidating any registered key schedules thequery to rerun. Mutations wrap an effect and invalidate keys only after itsucceeds. Keys can be a flat array, or a record whose property names act asbroad namespaces and whose ids address individual records.Common tasks- Provide the default in-memory service with {@link layer}.- Use {@link query} when callers need a queue of rerun results.- Use {@link stream} when downstream code should consume reruns as a stream.- Wrap writes with {@link mutation}, or call {@link invalidate} directly when invalidation is already part of the workflow.- Use the {@link Reactivity} service directly when many invalidations should be coalesced until a batch exits.Gotchas- The default layer is process-local; it does not coordinate invalidations across processes or cluster runners.- Non-primitive keys are matched by their Hash.hash value, so prefer stable key values over mutable objects.- If a query fails, its queue or stream fails with the same cause.- Invalidations that arrive while a query is already running coalesce into one follow-up run.See also- {@link query}, {@link stream}, {@link mutation}, and {@link invalidate}- {@link layer} and {@link Reactivity}
@effect/atom-react/Hooks
React hooks for reading, writing, mounting, refreshing, and subscribing toEffect atoms from the registry provided by RegistryContext.Common tasks- Read atom values in React components with {@link useAtomValue}- Read and write writable atoms with {@link useAtom}- Write without subscribing to the value with {@link useAtomSet}- Seed registry-local initial values with {@link useAtomInitialValues}- Integrate AsyncResult atoms with React Suspense through {@link useAtomSuspense}- Subscribe to atom changes or derive stable AtomRef propertiesGotchas- Hooks use the current RegistryContext, so each provider has an independent atom registry- Writable atoms are mounted by the write-oriented hooks before updates are sent- Suspense support throws promises for initial or waiting AsyncResult values and defects for failures unless includeFailure is enabled
@effect/atom-react/ReactHydration
React helpers for hydrating Atom registry state that was serialized on theserver or produced by a previous render. This module exposes{@link HydrationBoundary}, a client component that receives dehydrated Atomvalues and applies them to the nearest {@link RegistryContext} beforerendering children when it is safe to do so.Common use cases- Reusing Atom values that were collected during server rendering- Restoring client-side Atom state around a routed subtree- Keeping Atom-backed React trees consistent during hydration and transitionsReact gotchas- New Atom values can be hydrated during render so children see them immediately.- Existing Atom values are queued until after commit to avoid updating the current UI with transition data that might later be discarded.- Hydration is idempotent, so repeated or older dehydrated values are safe to pass through the boundary.
@effect/atom-react/ReactHydration
The RegistryContext module provides the React context used by Effect Atom
hooks to share an AtomRegistry across a component tree. The registry owns
atom state, scheduling, and idle cleanup, so components that read or write
atoms can coordinate through the same runtime instead of each creating an
isolated registry.
Common tasks
- Use {@link RegistryProvider} to scope atom state to a React subtree
- Seed atoms for tests, stories, or server-provided data with
initialValues - Override scheduling or idle timing for custom rendering environments
- Read {@link RegistryContext} when integrating lower-level atom APIs
Gotchas
- This is a client module because it depends on React runtime hooks and the scheduler package
- A provider keeps the registry stable across renders and disposes it shortly after unmount, allowing React remounts to reuse the same registry
- Overriding
scheduleTaskchanges when atom work is flushed, so it should return a cancellation function compatible with React unmounts
@effect/atom-react/ScopedAtom
The ScopedAtom module provides a small React integration for creating atominstances that are scoped to a component subtree. A scoped atom bundles aReact provider, context, and use accessor so each mounted provider owns itsown atom instance instead of sharing a single module-level atom.Use ScopedAtom when an atom needs to be isolated per feature, route,component instance, test harness, or provider input. The provider may receivean initial value that is passed to the atom factory, making it useful forstate that should be seeded from React props while still being consumed by theatom hooks in descendants.Gotchas- use must be called under the matching provider or it throws.- The provider creates the atom once for its lifetime; changing the provider value prop after mount does not recreate the atom.
Further more I added this exact prompt to ./ts/