Componentes Frontend
Estructura de Componentes
El proyecto utiliza una arquitectura basada en componentes con React. Los componentes están organizados principalmente en el directorio src/components/ y siguen un enfoque modular para facilitar la reutilización y mantenimiento.
Ejemplo de Componente
HomeHeroCarouselSlice
Este componente muestra un banner principal en la página de inicio, con soporte para imágenes, textos, enlaces y estilos personalizados.
import Image from "@components/image/image";
import {MOBILE_MEDIA_QUERY, useMediaQuery} from "@/hooks/use-media-query";
import Button from "@components/button/button";
import {useEffect, useState} from "react";
import clsx from "clsx";
import {useAnalytics} from "use-analytics";
import {Collections} from "@directus-schema/generated/client";
import {getDirectusAssetUrl} from "@/services/directus";
type HomeHeroCarouselSliceProps = {
slice: Collections.HomeHeroCarouselSlice,
height: string | number,
customId?: string | number,
}
const HomeHeroCarouselSlice = ({ slice, height, customId }: HomeHeroCarouselSliceProps) => {
// Estado y lógica del componente
return (
<article className="relative block w-full overflow-hidden">
{/* Contenido del componente */}
</article>
)
}
export default HomeHeroCarouselSlice
Props y Estados
Patrones de Props
El proyecto utiliza principalmente TypeScript para tipar las props de los componentes:
type ProductCardProps = {
product: ProductType;
showPrice?: boolean;
showRating?: boolean;
onProductClick?: (sku: string) => void;
}
Estado Global vs Local
- Estado Local: Usado para UI y comportamientos específicos del componente
- Estado Global: Gestionado con React Context para datos compartidos (carrito, usuario, etc.)
- Estado del Servidor: Datos obtenidos de APIs externas mediante SWR o SSR
Ejemplo de Context
// Contexto de producto
export type ProductContextType = {
product: ProductType | null;
loading: boolean;
error: Error | null;
relatedProducts: ProductType[];
fetchProduct: (sku: string) => Promise<void>;
};
export const ProductContext = createContext<ProductContextType | undefined>(undefined);
export function useProductContext() {
const context = useContext(ProductContext);
if (!context) {
throw new Error("useProductContext must be used within ProductProvider");
}
return context;
}
Dependencias entre Componentes
- Los componentes comparten un sistema de diseño común basado en Tailwind CSS
- Algunos componentes dependen de contextos globales (producto, carrito, etc.)
- La estructura es modular, facilitando reutilización
Ejemplos de Uso
Implementación de un Carrusel de Productos
import ProductCard from '@components/productCard/productCard';
import Carousel from 'react-elastic-carousel';
const ProductCarousel = ({ products, title }) => {
return (
<div className="my-8">
<h2 className="text-2xl font-bold mb-4">{title}</h2>
<Carousel itemsToShow={4} itemsToScroll={1} pagination={false}>
{products.map(product => (
<ProductCard key={product.sku} product={product} />
))}
</Carousel>
</div>
);
};
Integración con APIs
import { useEffect, useState } from 'react';
import TypesenseService from '@/services/typesense-service';
import ProductGrid from '@components/productGrid';
import LoadingSpinner from '@components/spinner';
const CategoryPage = ({ categoryId }) => {
const [products, setProducts] = useState([]);
const [loading, setLoading] = useState(true);
useEffect(() => {
const fetchProducts = async () => {
setLoading(true);
try {
const results = await TypesenseService.search({
q: '*',
filter_by: `categories:${categoryId} && is_available:=true`,
sort_by: 'popularity:desc',
per_page: 24
});
setProducts(TypesenseService.getHits(results));
} catch (error) {
console.error("Error fetching products:", error);
} finally {
setLoading(false);
}
};
fetchProducts();
}, [categoryId]);
if (loading) return <LoadingSpinner />;
return <ProductGrid products={products} />;
};