Files
shopify-template/hooks/use-shopify-products.ts
2026-08-08 14:21:24 -04:00

135 lines
3.6 KiB
TypeScript

'use client';
import { useState, useEffect, useCallback } from 'react';
import {
getProducts,
getProduct,
getProductRecommendations,
} from '@/services/shopify/catalog';
// Pure fetchers live in services/shopify/catalog so server code can use them
// too; re-exported here so existing imports keep working.
export {
getProducts,
getProductsPage,
getProduct,
getProductRecommendations,
} from '@/services/shopify/catalog';
export type {
Product,
ProductOption,
ProductOptionValue,
ProductsPage,
} from '@/services/shopify/catalog';
import type { Product } from '@/services/shopify/catalog';
interface UseProductsOptions {
first?: number;
after?: string | null;
query?: string;
sortKey?: 'BEST_SELLING' | 'CREATED_AT' | 'PRICE' | 'TITLE';
reverse?: boolean;
}
interface UseProductsReturn {
products: Product[];
loading: boolean;
error: string | null;
refetch: () => Promise<void>;
}
// Hook for fetching multiple products
export function useProducts(options: UseProductsOptions = {}): UseProductsReturn {
const [products, setProducts] = useState<Product[]>([]);
const [loading, setLoading] = useState(true);
const [error, setError] = useState<string | null>(null);
const fetchProducts = useCallback(async () => {
try {
setLoading(true);
setError(null);
const data = await getProducts(options);
setProducts(data);
} catch (err) {
console.error('Error fetching products:', err);
setError(err instanceof Error ? err.message : 'Failed to load products');
} finally {
setLoading(false);
}
}, [options.first, options.query, options.sortKey, options.reverse]);
useEffect(() => {
fetchProducts();
}, [fetchProducts]);
return { products, loading, error, refetch: fetchProducts };
}
// Hook for fetching a single product
export function useProduct(handle: string | null) {
const [product, setProduct] = useState<Product | null>(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState<string | null>(null);
const fetchProduct = useCallback(async () => {
if (!handle) {
setLoading(false);
return;
}
try {
setLoading(true);
setError(null);
const data = await getProduct(handle);
setProduct(data);
if (!data) {
setError('Product not found');
}
} catch (err) {
console.error('Error fetching product:', err);
setError(err instanceof Error ? err.message : 'Failed to load product');
} finally {
setLoading(false);
}
}, [handle]);
useEffect(() => {
fetchProduct();
}, [fetchProduct]);
return { product, loading, error, refetch: fetchProduct };
}
// Hook for fetching product recommendations
export function useProductRecommendations(productId: string | null) {
const [recommendations, setRecommendations] = useState<Product[]>([]);
const [loading, setLoading] = useState(true);
const [error, setError] = useState<string | null>(null);
const fetchRecommendations = useCallback(async () => {
if (!productId) {
setLoading(false);
return;
}
try {
setLoading(true);
setError(null);
const data = await getProductRecommendations(productId);
setRecommendations(data);
} catch (err) {
console.error('Error fetching recommendations:', err);
setError(err instanceof Error ? err.message : 'Failed to load recommendations');
} finally {
setLoading(false);
}
}, [productId]);
useEffect(() => {
fetchRecommendations();
}, [fetchRecommendations]);
return { recommendations, loading, error, refetch: fetchRecommendations };
}