/**
* useMemories Hook
* Fetch and manage a list of memories with auto-refresh
*/
import { useState, useEffect, useCallback, useRef } from 'react';
import { useMemoryStackClient } from './useMemoryStack';
import type { Memory, ListMemoriesRequest, UseMemoriesOptions, UseMemoriesResult } from '../types';
/**
* Hook to fetch and manage memories list
*
* @example
* ```tsx
* function MemoriesList() {
* const { memories, isLoading, error, refetch, hasMore, loadMore } = useMemories({
* userId: 'user_123',
* limit: 20,
* });
*
* if (isLoading && memories.length === 0) {
* return ;
* }
*
* if (error) {
* return Error: {error.message};
* }
*
* return (
* }
* onEndReached={() => hasMore && loadMore()}
* refreshing={isLoading}
* onRefresh={refetch}
* />
* );
* }
* ```
*/
export function useMemories(options: UseMemoriesOptions = {}): UseMemoriesResult {
const client = useMemoryStackClient();
const [memories, setMemories] = useState([]);
const [isLoading, setIsLoading] = useState(true);
const [error, setError] = useState(null);
const [hasMore, setHasMore] = useState(true);
const cursorRef = useRef(null);
const isMountedRef = useRef(true);
const {
userId,
limit = 20,
autoRefresh = false,
refreshInterval = 30000,
} = options;
// Fetch memories
const fetchMemories = useCallback(async (isRefresh: boolean = false) => {
if (!client) return;
try {
setIsLoading(true);
setError(null);
const request: ListMemoriesRequest = {
limit,
...(userId && { user_id: userId }),
...(isRefresh ? {} : { cursor: cursorRef.current ?? undefined }),
};
const response = await client.listMemories(request);
if (!isMountedRef.current) return;
if (isRefresh) {
setMemories(response.results);
} else {
setMemories(prev => [...prev, ...response.results]);
}
cursorRef.current = response.next_cursor;
setHasMore(response.next_cursor !== null);
} catch (err) {
if (!isMountedRef.current) return;
setError(err instanceof Error ? err : new Error('Failed to fetch memories'));
} finally {
if (isMountedRef.current) {
setIsLoading(false);
}
}
}, [client, userId, limit]);
// Initial fetch
useEffect(() => {
isMountedRef.current = true;
cursorRef.current = null;
setMemories([]);
fetchMemories(true);
return () => {
isMountedRef.current = false;
};
}, [userId, limit]);
// Auto-refresh
useEffect(() => {
if (!autoRefresh || refreshInterval <= 0) return;
const interval = setInterval(() => {
cursorRef.current = null;
fetchMemories(true);
}, refreshInterval);
return () => clearInterval(interval);
}, [autoRefresh, refreshInterval, fetchMemories]);
// Refetch from beginning
const refetch = useCallback(async () => {
cursorRef.current = null;
setMemories([]);
await fetchMemories(true);
}, [fetchMemories]);
// Load more (pagination)
const loadMore = useCallback(async () => {
if (!hasMore || isLoading) return;
await fetchMemories(false);
}, [fetchMemories, hasMore, isLoading]);
return {
memories,
isLoading,
error,
refetch,
hasMore,
loadMore,
};
}
export default useMemories;