@ai-growth/nextjs
Version:
Seamlessly integrate Sanity CMS with Next.js applications for automated blog routing and rendering
255 lines (254 loc) • 8.76 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.useCmsContent = useCmsContent;
exports.useCmsContentById = useCmsContentById;
const react_1 = require("react");
const utils_1 = require("../utils");
const contentCache = new Map();
/**
* React hook for fetching CMS content by slug with comprehensive state management
*/
function useCmsContent(slug, options = {}) {
const { contentType = 'post', initialData = null, enabled = true, staleTime = 5 * 60 * 1000, // 5 minutes
cacheTime = 10 * 60 * 1000, // 10 minutes
refetchOnWindowFocus = false, refetchOnReconnect = true, onSuccess, onError, ...fetchOptions } = options;
const [content, setContent] = (0, react_1.useState)(initialData);
const [isLoading, setIsLoading] = (0, react_1.useState)(!initialData && enabled && !!slug);
const [isError, setIsError] = (0, react_1.useState)(false);
const [error, setError] = (0, react_1.useState)(null);
const [isFetching, setIsFetching] = (0, react_1.useState)(false);
// Generate cache key
const cacheKey = (0, react_1.useMemo)(() => {
if (!slug)
return null;
return `${contentType}:${slug}:${JSON.stringify(fetchOptions)}`;
}, [slug, contentType, fetchOptions]);
// Check if cached data is stale
const isStale = (0, react_1.useMemo)(() => {
if (!cacheKey)
return false;
const cached = contentCache.get(cacheKey);
if (!cached)
return true;
return Date.now() - cached.timestamp > cached.staleTime;
}, [cacheKey, content]);
// Fetch content function
const fetchContent = (0, react_1.useCallback)(async () => {
if (!slug || !enabled || !cacheKey)
return;
// Check cache first
const cached = contentCache.get(cacheKey);
if (cached && !isStale && !isFetching) {
setContent(cached.data);
setIsLoading(false);
setIsError(false);
setError(null);
return;
}
try {
setIsFetching(true);
setIsError(false);
setError(null);
// Use the appropriate content fetching function
const data = await (0, utils_1.fetchContentBySlug)(contentType, slug, fetchOptions);
if (data) {
// Cache the result
contentCache.set(cacheKey, {
data,
timestamp: Date.now(),
staleTime,
});
setContent(data);
setIsLoading(false);
onSuccess?.(data);
}
else {
throw new Error(`Content not found for slug: ${slug}`);
}
}
catch (err) {
const errorObj = err instanceof Error ? err : new Error(String(err));
setError(errorObj);
setIsError(true);
setContent(null);
setIsLoading(false);
onError?.(errorObj);
}
finally {
setIsFetching(false);
}
}, [
slug,
enabled,
cacheKey,
contentType,
fetchOptions,
staleTime,
isStale,
isFetching,
onSuccess,
onError,
]);
// Refetch function for manual refresh
const refetch = (0, react_1.useCallback)(async () => {
if (cacheKey) {
contentCache.delete(cacheKey); // Clear cache
}
await fetchContent();
}, [cacheKey, fetchContent]);
// Reset function to clear state
const reset = (0, react_1.useCallback)(() => {
setContent(initialData);
setIsLoading(!initialData && enabled && !!slug);
setIsError(false);
setError(null);
setIsFetching(false);
}, [initialData, enabled, slug]);
// Initial fetch effect
(0, react_1.useEffect)(() => {
if (slug && enabled) {
fetchContent();
}
}, [fetchContent]);
// Window focus refetch
(0, react_1.useEffect)(() => {
if (!refetchOnWindowFocus)
return;
const handleFocus = () => {
if (slug && enabled && isStale) {
fetchContent();
}
};
window.addEventListener('focus', handleFocus);
return () => window.removeEventListener('focus', handleFocus);
}, [refetchOnWindowFocus, slug, enabled, isStale, fetchContent]);
// Online/reconnect refetch
(0, react_1.useEffect)(() => {
if (!refetchOnReconnect)
return;
const handleOnline = () => {
if (slug && enabled && isStale) {
fetchContent();
}
};
window.addEventListener('online', handleOnline);
return () => window.removeEventListener('online', handleOnline);
}, [refetchOnReconnect, slug, enabled, isStale, fetchContent]);
// Cache cleanup
(0, react_1.useEffect)(() => {
const cleanup = () => {
const now = Date.now();
for (const [key, entry] of contentCache.entries()) {
if (now - entry.timestamp > cacheTime) {
contentCache.delete(key);
}
}
};
const interval = setInterval(cleanup, cacheTime);
return () => clearInterval(interval);
}, [cacheTime]);
return {
content,
isLoading,
isError,
error,
isFetching,
isStale,
refetch,
reset,
};
}
/**
* React hook for fetching CMS content by ID
*/
function useCmsContentById(id, options = {}) {
const { initialData = null, enabled = true, staleTime = 5 * 60 * 1000, onSuccess, onError, ...fetchOptions } = options;
const [content, setContent] = (0, react_1.useState)(initialData);
const [isLoading, setIsLoading] = (0, react_1.useState)(!initialData && enabled && !!id);
const [isError, setIsError] = (0, react_1.useState)(false);
const [error, setError] = (0, react_1.useState)(null);
const [isFetching, setIsFetching] = (0, react_1.useState)(false);
const cacheKey = (0, react_1.useMemo)(() => {
if (!id)
return null;
return `id:${id}:${JSON.stringify(fetchOptions)}`;
}, [id, fetchOptions]);
const isStale = (0, react_1.useMemo)(() => {
if (!cacheKey)
return false;
const cached = contentCache.get(cacheKey);
if (!cached)
return true;
return Date.now() - cached.timestamp > cached.staleTime;
}, [cacheKey, content, staleTime]);
const fetchContent = (0, react_1.useCallback)(async () => {
if (!id || !enabled || !cacheKey)
return;
const cached = contentCache.get(cacheKey);
if (cached && !isStale && !isFetching) {
setContent(cached.data);
setIsLoading(false);
setIsError(false);
setError(null);
return;
}
try {
setIsFetching(true);
setIsError(false);
setError(null);
const data = await (0, utils_1.fetchContentById)(id, fetchOptions);
if (data) {
contentCache.set(cacheKey, {
data,
timestamp: Date.now(),
staleTime,
});
setContent(data);
setIsLoading(false);
onSuccess?.(data);
}
else {
throw new Error(`Content not found for ID: ${id}`);
}
}
catch (err) {
const errorObj = err instanceof Error ? err : new Error(String(err));
setError(errorObj);
setIsError(true);
setContent(null);
setIsLoading(false);
onError?.(errorObj);
}
finally {
setIsFetching(false);
}
}, [id, enabled, cacheKey, fetchOptions, staleTime, isStale, isFetching, onSuccess, onError]);
const refetch = (0, react_1.useCallback)(async () => {
if (cacheKey) {
contentCache.delete(cacheKey);
}
await fetchContent();
}, [cacheKey, fetchContent]);
const reset = (0, react_1.useCallback)(() => {
setContent(initialData);
setIsLoading(!initialData && enabled && !!id);
setIsError(false);
setError(null);
setIsFetching(false);
}, [initialData, enabled, id]);
(0, react_1.useEffect)(() => {
if (id && enabled) {
fetchContent();
}
}, [fetchContent]);
return {
content,
isLoading,
isError,
error,
isFetching,
isStale,
refetch,
reset,
};
}