--- name: jotai-reactive-atoms description: Implement reactive state management with Jotai atoms in Electron renderer. Use when creating atoms, implementing real-time updates via IPC, or setting up hybrid stream + HTTP fallback patterns. Covers event subscription optimization, debouncing, and Suspense integration. allowed-tools: Read, Write, Edit, Grep, Glob --- # Jotai Reactive Atoms for Electron ## Overview This skill documents the Jotai atom patterns for Electron applications with IPC-based real-time updates. ## Key Patterns | Pattern | Use Case | Description | |---------|----------|-------------| | Hybrid Atom | Real-time data | Stream + HTTP fallback | | Stream Atom | IPC subscriptions | onMount with subscription | | Single Fetch Atom | Initial data | HTTP-only async atom | | Event Subscription | IPC optimization | Shared IPC listeners | | Write Atom | Mutations (CRUD) | Create, Update, Delete operations | ## Quick Reference ### Hybrid Atom Pattern ```typescript // 1. HTTP fallback (initial load) const singleFetchAtom = atomWithRefresh(async () => { const res = await client.users.$get(); return res.json(); }); // 2. Stream atom (IPC updates) - NO initial fetch const streamAtom = atom<{ value: User[] }>(); streamAtom.onMount = (set) => { const handleUpdate = debounce(async () => { const res = await client.users.$get(); if (res.ok) set({ value: await res.json() }); }, 300); return usersSource.subscribe(handleUpdate); }; // 3. Hybrid selector (exported) - NOT async export const usersAtom = atom((get) => { const stream = get(streamAtom); if (stream !== undefined) return stream.value; return get(singleFetchAtom); }); ``` ### Write Atom Pattern ```typescript export const createUserAtom = atom( null, // No read value async (_get, set, data: CreateUserData) => { const res = await client.users.$post({ json: data }); switch (res.status) { case 201: { set(singleFetchAtom); // Refresh read atom return res.json(); } default: throw new Error('Failed'); } } ); // Usage: const [, createUser] = useAtom(createUserAtom); ``` ### Component Usage ```tsx const UserList = () => { const users = useAtomValue(usersAtom); const [, createUser] = useAtom(createUserAtom); // ... }; export const UsersPage = () => ( }> ); ``` ## When to Use Each Pattern ### Hybrid Atom (Stream + HTTP Fallback) Use for data that: - Changes frequently - Needs real-time updates - Is shared across components Examples: Users, Notifications, Active Event ### Stream-Only Atom Use for data that: - Is append-only (like logs) - Doesn't need initial fetch ### Single Fetch Atom Use for data that: - Rarely changes - Doesn't need real-time updates ## Key Rules 1. **Hybrid atom getter must NOT be async** - See [Why not use async](HYBRID-ATOM.md#why-not-use-async---understanding-suspense-behavior) 2. **No initial fetch in onMount** - singleFetchAtom handles initial load 3. **Always debounce IPC handlers** - prevents excessive fetches 4. **Use switch(res.status)** - not res.ok 5. **Refresh read atoms after mutations** - `set(singleFetchAtom)` ## Detailed Documentation - [HYBRID-ATOM.md](HYBRID-ATOM.md) - Full hybrid pattern with diagrams - [WRITE-ATOM.md](WRITE-ATOM.md) - Write atom patterns (create, update, delete) - [EVENT-SUBSCRIPTION.md](EVENT-SUBSCRIPTION.md) - IPC subscription optimization - [examples/hybrid-atom.ts](examples/hybrid-atom.ts) - Read atom implementation - [examples/write-atom.ts](examples/write-atom.ts) - Write atom implementation - [examples/use-optimistic-value.ts](examples/use-optimistic-value.ts) - Optimistic update hook ## Related Skills - [suspense-boundary-design](../suspense-boundary-design/SKILL.md) - Suspense boundary placement and ErrorBoundary patterns