Saltar al contenido principal

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} />;
};