AGENT SKILL

01-atoms

Effect Atom reactivity — useAtomValue, Atom.make, React integration via @effect/atom-react

使用场景前端开发
License 许可
MIT
最近更新
2026-05-31
Stars
★ 0
Forks
0

01 前言 Front Matter

name
01-atoms
license
MIT
description
Effect Atom reactivity — useAtomValue, Atom.make, React integration via @effect/atom-react
compatibility
opencode

03 SKILL.md 正文

Effect Atom (Reactivity)

Package: effect/unstable/reactivity, @effect/atom-react Version: effect@4.0.0-beta.41, @effect/atom-react@4.0.0-beta.41

Runtime setup

import { Layer } from "effect";
import { DevTools } from "effect/unstable/devtools";
import { FetchHttpClient } from "effect/unstable/http";
import { Atom } from "effect/unstable/reactivity";

// Static site — no RPC server, just FetchHttpClient
export const runtime = Atom.runtime(
  FetchHttpClient.layer.pipe(
    Layer.provideMerge(import.meta.env.VITE_ENABLE_DEVTOOLS === "true" ? DevTools.layer() : Layer.empty),
  ),
);

Atom types

// runtime.atom(Effect) — single-shot, loads once, reactive
export const benchmarkAtom = runtime.atom(
  Effect.gen(function* () {
    const client = yield* HttpClient.HttpClient;
    const response = yield* client.get(url);
    const body = yield* response.json;
    return yield* Schema.decode(BenchmarkDataSchema)(body);
  }),
);
// Type: Atom<AsyncResult<BenchmarkData, E>>

// runtime.fn(arg => Effect) — triggered/parameterized atom
export const searchAtom = runtime.fn((query: string) =>
  Effect.gen(function* () {
    const svc = yield* SearchService;
    return yield* svc.search(query);
  }),
);
// Type: AtomResultFn<string, SearchResult, E>

// runtime.atom(Stream) — streaming atom
export const streamAtom = runtime.atom(Stream.fromPubSub(myPubSub));

React hooks

import { useAtom, useAtomSet, useAtomValue } from "@effect/atom-react";
import { AsyncResult } from "effect/unstable/reactivity";

// Read-only
const result = useAtomValue(benchmarkAtom);

// Read + write (returns [value, setter])
const [result, trigger] = useAtom(searchAtom);
trigger("my query");

// Write-only
const trigger = useAtomSet(searchAtom);
trigger("my query");

AsyncResult pattern matching

import { AsyncResult } from "effect/unstable/reactivity";

// In a React component
const result = useAtomValue(benchmarkAtom);

return AsyncResult.match(result, {
  onInitial: (_) => <Loading />,
  onFailure: (e) => <Error message={String(e.cause)} />,
  onSuccess: (s) => <Leaderboard data={s.value} />,
});

// getOrElse for a default
const data = AsyncResult.getOrElse(result, () => emptyBenchmarkData);

RegistryProvider — required in app root

// main.tsx — not needed explicitly when using Atom.runtime()
// The runtime() call registers itself globally.
// But for tests, wrap with RegistryProvider:
import { RegistryProvider } from "@effect/atom-react";

render(
  <RegistryProvider>
    <App />
  </RegistryProvider>,
);

Key signatures from Atom.d.ts

// AtomRuntime.atom — wraps Effect or Stream → AsyncResult atom
runtime.atom(effect: Effect<A, E, R | AtomRegistry>): Atom<AsyncResult<A, E>>

// AtomRuntime.fn — triggered atom
runtime.fn(fn: (arg: Arg) => Effect<A, E, R | AtomRegistry>): AtomResultFn<Arg, A, E>

// AsyncResult states
type AsyncResult<A, E> = Initial<A, E> | Success<A, E> | Failure<A, E>

// Hooks
useAtomValue<A>(atom: Atom<A>): A
useAtom<R,W>(atom: Writable<R,W>): readonly [R, (value: W) => void]
useAtomSet<R,W>(atom: Writable<R,W>): (value: W) => void

04 同仓库更多 Skills