import { useCallback, useEffect, useState } from 'react'; import type { Context } from '../types'; import type { ModuleAdapter } from './types'; /** * React hook for managing a list of module items * * Provides loading state, error handling, and automatic refresh * Supports context-aware listing and realtime subscriptions * * @example * ```tsx * const { items, loading, error, refresh } = useModuleList(NoteAdapter); * * // With context * const { items } = useModuleList(NoteAdapter, { * context: { type: 'project', id: '123' } * }); * ``` */ export function useModuleList( adapter: ModuleAdapter, opts?: { context?: Context } ) { const [items, setItems] = useState([]); const [loading, setLoading] = useState(true); const [error, setError] = useState(null); // Derive a stable key from context values so deps don't change with object identity const ctxKey = opts?.context ? `${opts.context.type}:${opts.context.id}` : ''; const refresh = useCallback(async () => { setLoading(true); setError(null); try { const data = await adapter.list({ context: opts?.context }); setItems(Array.isArray(data) ? data : []); return { success: true as const }; } catch (err) { console.error(err); setItems([]); const msg = err instanceof Error ? err.message : 'Failed to load items'; setError(msg); return { success: false as const, error: msg }; } finally { setLoading(false); } }, [adapter, ctxKey]); useEffect(() => { refresh(); }, [refresh]); useEffect(() => { if (!adapter.subscribe) return; try { const unsub = adapter.subscribe(async () => { await refresh(); }, { context: opts?.context }); return () => { try { unsub && unsub(); } catch { /* noop */ } }; } catch { return () => {}; } }, [adapter, ctxKey, refresh]); return { items, loading, error, refresh } as const; }