terry-staging #11
@@ -94,6 +94,24 @@ function buildRealUrl(params: PostStreamParams, cursor?: string): string {
|
|||||||
return `${q ? "/api/posts/search" : "/api/posts"}?${sp.toString()}`;
|
return `${q ? "/api/posts/search" : "/api/posts"}?${sp.toString()}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type CachedStream = {
|
||||||
|
items: Post[];
|
||||||
|
cursor: string | undefined;
|
||||||
|
hasMore: boolean;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Session cache of loaded stream state, keyed by the request params. Lets a
|
||||||
|
* view that's been seen before (e.g. switching Home ⇄ All) restore instantly —
|
||||||
|
* all loaded pages, no skeleton, no refetch — instead of reloading from page 1.
|
||||||
|
* In-memory only: a full page reload starts fresh.
|
||||||
|
*/
|
||||||
|
const streamCache = new Map<string, CachedStream>();
|
||||||
|
|
||||||
|
function streamKey(params: PostStreamParams): string {
|
||||||
|
return buildRealUrl(params);
|
||||||
|
}
|
||||||
|
|
||||||
export function usePostStream(params: PostStreamParams): PostStreamResult {
|
export function usePostStream(params: PostStreamParams): PostStreamResult {
|
||||||
const [items, setItems] = useState<Post[]>([]);
|
const [items, setItems] = useState<Post[]>([]);
|
||||||
const [hasMore, setHasMore] = useState(true);
|
const [hasMore, setHasMore] = useState(true);
|
||||||
@@ -172,6 +190,17 @@ export function usePostStream(params: PostStreamParams): PostStreamResult {
|
|||||||
);
|
);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
// Restore a previously-loaded view instantly (no reset, no refetch).
|
||||||
|
const cached = streamCache.get(streamKey(params));
|
||||||
|
if (cached) {
|
||||||
|
setItems(cached.items);
|
||||||
|
cursorRef.current = cached.cursor;
|
||||||
|
setHasMore(cached.hasMore);
|
||||||
|
hasMoreRef.current = cached.hasMore;
|
||||||
|
setIsLoading(false);
|
||||||
|
setError(null);
|
||||||
|
return;
|
||||||
|
}
|
||||||
setItems([]);
|
setItems([]);
|
||||||
cursorRef.current = undefined;
|
cursorRef.current = undefined;
|
||||||
setHasMore(true);
|
setHasMore(true);
|
||||||
@@ -188,6 +217,17 @@ export function usePostStream(params: PostStreamParams): PostStreamResult {
|
|||||||
params.lang,
|
params.lang,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
// Persist loaded state so returning to this view restores it from cache.
|
||||||
|
useEffect(() => {
|
||||||
|
if (items.length === 0) return;
|
||||||
|
streamCache.set(streamKey(params), {
|
||||||
|
items,
|
||||||
|
cursor: cursorRef.current,
|
||||||
|
hasMore,
|
||||||
|
});
|
||||||
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
|
}, [items, hasMore]);
|
||||||
|
|
||||||
const loadMore = useCallback(() => {
|
const loadMore = useCallback(() => {
|
||||||
fetchPage(false);
|
fetchPage(false);
|
||||||
}, [fetchPage]);
|
}, [fetchPage]);
|
||||||
|
|||||||
Reference in New Issue
Block a user