docs: React patterns with @effect/atom-react + example app reference implementation - #64
Conversation
Introduce REACT.md and reference implementation in example/app for using effect-firebase repositories from React: a RuntimeProvider that binds a Firestore Layer to a ManagedRuntime, plus useEffectQuery, useEffectStream, and useEffectMutation hooks that surface a Result- shaped state and handle fiber cleanup. Refactor the /firestore route to use the hooks together with @tanstack/react-form and effect/Schema (via toStandardSchemaV1) for validated CRUD. Add a Vitest setup with @testing-library/react and a demo test that swaps in @effect-firebase/mock at the provider boundary. The hook surface intentionally mirrors @effect-atom/atom-react idioms so a future migration is mechanical once atom-react supports Effect v4. Also remove a duplicate deleteRecursive key in the mock service that blocked the package build. https://claude.ai/code/session_01R1D97BwwWVdGzARJY8iWeB
Replace the hand-rolled RuntimeProvider + useEffectQuery/Stream/Mutation
hooks with the upstream Effect-TS React binding now that
@effect/atom-react has shipped for Effect v4.
Repository operations live in example/app/src/lib/atoms.ts as atoms
keyed by a swappable firestoreLayerAtom: postByIdAtom, postByIdLiveAtom,
latestPostsAtom, and addPost/updatePost/deletePostAtom mutations. The
firestore route reads via useAtomValue + AsyncResult.builder and writes
via useAtomSet({ mode: 'promise' }). RegistryProvider replaces the
custom provider; tests swap the layer via initialValues. REACT.md is
rewritten to document the atom-based patterns.
https://claude.ai/code/session_01R1D97BwwWVdGzARJY8iWeB
Add a new section to REACT.md that compares the atom-based patterns to TanStack Query side-by-side, including code excerpts for the live-posts case, lists of where each approach wins, and conditional recommendations based on the consumer's situation. https://claude.ai/code/session_01R1D97BwwWVdGzARJY8iWeB
…l-qgjsm4 # Conflicts: # example/app/package.json # pnpm-lock.yaml
Fixes found by review of the React patterns work:
- Wrap firestoreLayerAtom in Atom.keepAlive. The registry GCs
non-keepAlive atoms with no subscribers, so the seeded production
layer was dropped moments after mount whenever the first route read
no atoms; navigating to /firestore afterwards rebuilt the runtime
from the default layer and every read died with a missing-service
defect.
- Replace the unsound `Layer.empty as unknown as ...` default with a
typed Layer.effect(FirestoreService, Effect.die(...)) that carries
the seeding instruction, so a forgotten seed fails actionably and
the cast no longer silences the type error.
- Pass { concurrent: true } to the mutation fn atoms. The default
interrupts the in-flight previous invocation (latest-wins), so two
quick deletes could drop the first write while its awaiter resolved
with the second's result.
- Wire invalidation: reactivityKeys on mutations plus
Atom.withReactivity on the one-shot postByIdAtom, and an idle TTL on
postByIdLiveAtom so per-id listeners don't linger forever.
- Surface mutation failures again (lost in the atom refactor): submit
errors are caught in onSubmit and rendered (form-core rethrows out
of handleSubmit, so they previously became unhandled rejections with
no UI feedback), and delete failures render an error instead of
rejecting unobserved.
- Restore the Cancel-disabled-while-submitting guard and the
scroll-to-form on edit from the pre-atom implementation.
- Revert tsconfig.spec.json to test-only includes with a project
reference to tsconfig.app.json, matching the sibling packages;
the widened include made every app source a member of two composite
projects emitting the same declaration outputs and leaked
vitest/globals into production code's type environment.
- Cleanups: switch AsyncResult.builder to .exhaustive(), hoist the
Intl.DateTimeFormat and Standard Schema validator to module scope,
drop the dead Option/Query re-export, repoEffect alias, AtomContext
annotation, and the redundant initialValues useMemo.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CvEmdZqNcYD2rEVniWLXjt
- Fix three factually wrong claims verified against the shipped APIs: RegistryProvider reads initialValues only at registry creation (it never rebuilds on layer identity changes); atoms are disposed immediately on last unsubscribe unless keepAlive/setIdleTTL/ defaultIdleTTL is set (no implicit idle TTL, and streams stay live during a TTL window rather than pausing); AsyncResult.builder only enforces exhaustiveness via .exhaustive() while .render() compiles with missing handlers and rethrows unhandled failures at runtime. - Update the runtime-setup and repository-atom samples to match the hardened implementation: keepAlive + dying default layer instead of the unsound cast, withReactivity/reactivityKeys invalidation, concurrent mutations, idle TTL on the per-id live family. - Add failure handling to the form sample and document that form-core rethrows onSubmit errors out of handleSubmit. - Fix the keyed-read sample's non-compiling `id: PostId` type (PostId is a schema value; use `typeof PostId.Type`). - Drop the duplicated devtools bullet from "Where it's a wash" (it is already listed as a Query win), and point the atom-react link at npm instead of the effect-smol staging repo. - Link REACT.md and MIGRATION.md from the README so the guides are discoverable. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CvEmdZqNcYD2rEVniWLXjt
|
Warning Review limit reached
Next review available in: 46 minutes Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available. How can I continue?After more reviews become available, a review can be triggered using the To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews. How do review limits work?CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability. For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window. Please refer docs for additional details. Review details⚙️ Run configurationConfiguration used: Repository UI Review profile: ASSERTIVE Plan: Pro Run ID: ⛔ Files ignored due to path filters (1)
📒 Files selected for processing (1)
📝 WalkthroughWalkthroughThe PR adds React integration documentation and rewires the Firestore example to use registry-seeded Effect runtimes, reactive repository atoms, atom-based CRUD components, Vitest configuration, and mocked Firestore coverage. ChangesReact Firestore integration
Estimated code review effort: 4 (Complex) | ~45 minutes Sequence Diagram(s)sequenceDiagram
participant App
participant RegistryProvider
participant PostForm
participant PostList
participant PostRepository
App->>RegistryProvider: seed memoized Firestore layer
RegistryProvider->>PostForm: provide atom runtime
RegistryProvider->>PostList: provide atom runtime
PostForm->>PostRepository: add or update post
PostList->>PostRepository: load or delete posts
PostRepository-->>PostList: refresh reactive post state
Suggested labels: Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
The guide is about how to use the atom patterns, not why to pick them over another library. Remove section 7 and renumber Caveats. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CvEmdZqNcYD2rEVniWLXjt
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@example/app/package.json`:
- Around line 12-35: Move `@effect-firebase/client` and `@example/shared` from
devDependencies into dependencies alongside effect-firebase, since they are
imported at runtime by app.tsx, atoms.ts, and firestore.tsx. Remove the
duplicate vite entry from devDependencies while keeping its dependencies entry
unchanged.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository UI
Review profile: ASSERTIVE
Plan: Pro
Run ID: 79dab701-5a0c-4cc4-83ee-78bb3a944260
⛔ Files ignored due to path filters (1)
pnpm-lock.yamlis excluded by!**/pnpm-lock.yaml
📒 Files selected for processing (11)
REACT.mdREADME.mdexample/app/package.jsonexample/app/src/__tests__/firestore.test.tsxexample/app/src/app/app.tsxexample/app/src/lib/atoms.tsexample/app/src/routes/firestore.tsxexample/app/tsconfig.app.jsonexample/app/tsconfig.spec.jsonexample/app/vite.config.tspnpm-workspace.yaml
Move @effect-firebase/client and @example/shared to dependencies (both are runtime imports), keep vite only in devDependencies instead of duplicated in both blocks, and drop the peerDependencies block — the app is private and the same packages are now real dependencies. Addresses CodeRabbit review feedback on #64. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CvEmdZqNcYD2rEVniWLXjt
Adds
REACT.md— a guide for using effect-firebase repositories from React with@effect/atom-react— plus a reference implementation inexample/app: repository atoms with a swappable Firestore layer, live queries, mutations, a validated TanStack form, and a test that swaps in@effect-firebase/mockat the registry boundary.Includes a review pass that hardened the implementation (keepAlive on the layer atom, concurrent mutations with reactivity keys, restored error feedback on failed writes) and corrected doc claims against the shipped beta.99 APIs.
Build, tests, and lint are green across the workspace.
🤖 Generated with Claude Code
https://claude.ai/code/session_01CvEmdZqNcYD2rEVniWLXjt
Generated by Claude Code
Summary by CodeRabbit
New Features
Tests