No tienes acceso a esta clase

¡Continúa aprendiendo! Únete y comienza a potenciar tu carrera

Evitando productos duplicados en el carrito

18/31
Recursos

Aportes 25

Preguntas 4

Ordenar por:

¿Quieres ver más aportes, preguntas y respuestas de la comunidad?

o inicia sesión.

por si alguien le sirve, yo desde antes habia creado es una funcion que agrega los productos al carrito y si se vuelve a dar al mismo producto lo que hace es sumar la cantidad y el precio

  //Agrega un producto al carrito, y si ya existe aumenta la cantidad y suma los productos
  const addProduct = payload => {
    const productIndex = cart.findIndex(product => product.id === payload.id)
    let newCart = []
    if (productIndex >= 0) {
      newCart = [...cart]
      newCart[productIndex].quantity++
      newCart[productIndex].price = payload.price + newCart[productIndex].price
    } else {
      newCart = [...cart, { ...payload, quantity: 1 }]
    }
    setCart(newCart)
    getTotalInfo(newCart)
    openCheckoutSideMenu()
  }

Les comparto como lo realicé haciendo uso del método some, el cual directamente nos devuelve el booleano de si hay al menos un elemento que cumpla con la condición que le damos:

Yo agregué al bg una transparencia:

className='absolute top-0 right-0 flex justify-center items-center bg-black/50 w-6 h-6 rounded-full m-2 p-1'>

Me parece que esa función auxiliar renderIcon que estamos definiendo dentro del mismo componente debería hacerse por fuera del mismo, incluso como un componente aparte. Así evitamos que react redefina la función en cada re-render del componente padre, y dejamos que react decida cuándo re-renderizar el componente hijo, así:

import { useContext } from "react"
import { PlusIcon, CheckIcon } from '@heroicons/react/24/solid'
import { ShoppingCartContext } from "../../context"

const AddItemButton = ({ isInCart, onItemAdded }) => {
  if ( isInCart ) {
    return (
      <div className="absolute top-0 right-0 flex justify-center items-center bg-black w-6 h-6 rounded-full m-2 p-1">
        <CheckIcon className="w-6 h-6 text-white" />
      </div>
    )
  }

  return (
    <div
      className="absolute top-0 right-0 flex justify-center items-center bg-white w-6 h-6 rounded-full m-2 p-1"
      onClick={onItemAdded}
    >
      <PlusIcon className="w-6 h-6 text-black" />
    </div>
  )
}

export default function Card({ item }) {
  const context = useContext(ShoppingCartContext)

  const showProduct = () => {
    context.setSelectedProduct(item)
    context.openProductDetails()
    context.closeCheckoutMenu()
  }

  const addToCart = (event) => {
    event.stopPropagation();

    const newCartProducts = [...context.cartProducts, item]
    context.setCartProducts(newCartProducts)
    context.setCount(context.count + 1)
    context.openCheckoutMenu()
    context.closeProductDetails()
  }

  const isInCart = !!context.cartProducts.find(product => product.id === item.id)

  return (
    <div
      className="bg-white cursor-pointer w-56 h-60"
      onClick={showProduct}
    >
      <figure className="relative mb-2 w-full h-4/5">
        <span className="absolute bottom-0 left-0 bg-white/60 rounded-full text-black text-xs px-3 py-1 m-2">
          { item.category.name }
        </span>
        <img className="w-full h-full object-cover rounded-lg" src={item.images[0]} alt={item.title} />

        <AddItemButton
          isInCart={isInCart}
          onItemAdded={addToCart}
        />
      </figure>

      <p className="flex justify-between items-center">
        <span className="text-sm font-light">
          { item.title }
        </span>
        <span className="text-lg font-medium">
          ${ item.price }
        </span>
      </p>
    </div>
  )
}

En este caso tal vez no haga gran diferencia, pero en otros escenarios ese patrón de definir componentes dentro de componentes puede causar bugs raros, por ejemplo esto:

https://levelup.gitconnected.com/code-review-avoid-declaring-react-component-inside-parent-component-1768a645f523

Asi va mi Cart

lo resumí un poco más sencillo: cree un estado isClicked el cual se cambia cuando se hace click en el más y dependiendo de ello se muestra o un icono u otro
![](```
const [isClicked, setIsClicked] = useState(false);

const handleAddToCart = (event) => {
event.stopPropagation();
context.setCart([…context.cart, data.data]);
context.setCount(context.count + 1);
context.handleOpenCheckout();
setIsClicked(true);

<button className={${isClicked ? 'hover:bg-red-600' :'hover:bg-blue-500'} absolute top-0 right-0 flex justify-centetr items-center bg-white w-6 h-6 rounded-full m-2 p-1}>
{isClicked ? (
<CheckIcon className="h-6 w-6 text-green-600 " />
) : (
<PlusCircleIcon
className="h-6 w-6 text-black hover:text-white "
onClick={(event) => handleAddToCart(event)}
/>
)}
</button>
};

En mi caso trate de no sobre cargar el componente card y cree un componente aparte para del boton de AddToCart sin repetir mucho codigo

Con SVG sin necesidad de instalar dependencias:

const renderIcon = (id) => {
    const isInCard = context.CartProducts.filter((product) => product.id === id).length > 0

    if (isInCard) {
      return (
        <button className="absolute top-0 right-0 flex justify-center items-center bg-black w-6 h-6 rounded-full m-2 p-1 cursor-pointer">
          {/* check icon */}
          <svg xmlns="https://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" className="w-6 h-6 text-white">
            <path fillRule="evenodd" d="M19.916 4.626a.75.75 0 01.208 1.04l-9 13.5a.75.75 0 01-1.154.114l-6-6a.75.75 0 011.06-1.06l5.353 5.353 8.493-12.739a.75.75 0 011.04-.208z" clipRule="evenodd" />
          </svg>
        </button>
      )
    } else {
      return (
        <button
          onClick={(event)=> addProductsToCart(event, data.data)}
          className="absolute top-0 right-0 flex justify-center items-center bg-white w-6 h-6 rounded-full m-2 p-1 cursor-pointer"
        >
          {/* + plus icon */}
          <svg xmlns="https://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" className="w-6 h-6">
            <path fillRule="evenodd" d="M12 3.75a.75.75 0 01.75.75v6.75h6.75a.75.75 0 010 1.5h-6.75v6.75a.75.75 0 01-1.5 0v-6.75H4.5a.75.75 0 010-1.5h6.75V4.5a.75.75 0 01.75-.75z" clipRule="evenodd" />
          </svg>
        </button>
      )
    }
  }

Una consulta

const addProductToCard=(e,productData)=>{
    
        const test={
            id:productData.id,
            price:productData.price,
            images:productData.images,
            title:productData.title,
            counter:productData.counter,
            priceref:productData.price
        }
        
        const checker=context.cartProducts.findIndex((prd)=>prd.id===test.id)
        if(checker!=-1){
            test.price=context.cartProducts[checker].price+productData.price
            context.cartProducts.splice(checker,1)
             
        }
        context.setCartProducts([...context.cartProducts,test])
        ;
        e.stopPropagation()
        context.setCount(context.count+1)        
        context.openCheckoutSideMenu()
    }
<code> 

Quería conseguir que los precios se sumaran cuando un cliente comprase varios productos del mismo tipo,pero tuve que crear un objeto adicional (const test)y copiar manualmente los valores del productData,ya que al modificar el productData, el Data.Data(objeto que se renderiza en la pantalla principal como Card) tambien se modificaba y terminaba con los precios sumados en la misma pantalla principal.

Alguien podria decirme por que pasa esto y como evitarlo o sortearlo sin tener que hacer otro objeto?

Por si a alguien le suena, no hice función:

<figure className='relative mb-5 w-full h-4/5'>
        <span className='absolute bottom-0 left-0 bg-white/80 rounded-lg text-black text-xs m-2 p-1'>
          Categorie {data?.volumeInfo?.categories}
        </span>
        <img
          className='w-full h-full object-fit rounded-lg'
          src={data?.data?.volumeInfo?.imageLinks?.thumbnail}
          alt='book'
        />
        {context.cartProducts.filter((product) => product.id === data?.data?.id)
          .length > 0 ? (
          <div className='absolute top-0 right-0 flex justify-center items-center text-xs bg-white w-6 h-6 rounded-full m-2'>
            <AiOutlineCheck className='h-5 w-5 text-green-600' />
          </div>
        ) : (
          <div
            className='absolute top-0 right-0 flex justify-center items-center text-xs bg-white/80 w-6 h-6 rounded-full m-2'
            onClick={(event) => addBookToCart(event, data?.data)}
          >
            <HiOutlinePlus />
          </div>
        )}
la lista de videos esta muy mal ubicado, me es incomodo bajar hasta abajo, pierdo tiempo y concentracion
Por que quitaron la fecha de los comentarios del curso? me era util para poder visualizar el ultimo comentario, al menos asi me entero lo desactualizado que es esta el curso en algunos comentarios se podia ver como que le falta al curso y si esta desactualizado con que lo actualizaron
¿De qué otra manera se puede hacer esto? Hacerlo con un filter es súper pesado para react hace como 4610 de verificaciones; por lo que el performance cae mucho

Asi vamos…

Yo hice una acumulación de los productos.

En el contexto agregue un metodo para hacer la acumulación

const addProductsToCart = (productData) =>{
        console.log("get",getProductsToCart([...cartProducts,productData]));
        setCartProducts(getProductsToCart([...cartProducts,productData]));
    }


    const getProductsToCart = (products) =>{
        const uniqueProducts = products.reduce((accumulator, product) => {
            const existingProduct = accumulator.find(item => item.id === product.id);
          
            if (existingProduct) {
              existingProduct.count ++;
            } else {
              accumulator.push({ id: product.id, count: product.count?product.count:1,image:product.image,price: product.price,title:product.title});
            }
          
            return accumulator;
          }, []);
          return uniqueProducts;
    }

En el ordercard

<div className="flex justify-between items-center">
            <div className="flex items-center gap-2">
                <figure className="w-8 h-8">
                    <img className="w-full h-full rounded-lg object-cover" src={imageUrl} alt={title} />
                </figure>
                <p className="text-medium font-light">{title} x{count}</p>
            </div>
            <div className="flex items-center gap-2">
                <p className="text-lg font-medium">{price}</p>
                <p className="text-lg font-medium">{price*count}</p>
                <XMarkIcon className="h-6 w-6 cursor-pointer"/>
            </div>
        </div>

Así va mi e-commerce

Lo que yo hice fue que; al darle click en el icono se agregue al carrito y al volver a darle click se salga del carrito, en ese caso que seria mejor?

  • que solo se agregue desde la tarjeta y que se quite el producto directamente desde el carrito

o

-como yo hice que se quite y se agrege desde la targeta del producto?

Creo que en el curso no se añaden los botones para aumentar o disminuir la cantidad de cada item añadido al carrito. Aquí las 2 funciones para hacer eso:
Van en el Contexto y se importan en la OrderCard.
En la OrderCard hay que pasar id como props porque se necesita el id para añadir o disminuir la cantidad:

    const quantityAdd = (productId) => {
        const updatedCartProducts = cartProducts.map((item) => {
            if (item.id === productId && item.quantity < 10) {
                return { ...item, quantity: item.quantity + 1 };
            }
            return item;
            });
            setCartProducts(updatedCartProducts);
    }
        
    const quantitySubtract = (productId) => {
        const updatedCartProducts = cartProducts.map((item) => {
        if (item.id === productId && item.quantity > 1) {
            return { ...item, quantity: item.quantity - 1 };
        }
        return item;
        });
        setCartProducts(updatedCartProducts);
    }

Por ahí habían compartido en comentarios en clases anteriores el código para añadir ‘cantidad’ al objeto de cada item a la hora de añadirlo a My Order.

const addProductsToCart = (product) =>{
    //Buscar product en el cart: true/false
    const productExists = cartProducts.some(p => p.id === product.id);
    console.log('product exists: '+productExists);

    //Si el producto existe:
    if (productExists) {
			const productCart = cartProducts.find(p => p.id === product.id); // Busca el producto
			productCart.quantity += 1; // Cantidad = +1
		} else {
			product.quantity = 1; // Si no, crea la propiedad quantity = 1.
      setCartProducts([...cartProducts, product]);
		}

    setCounter(counter+1);
    closeProductDetail();
    openCheckoutSM();
  }

el mi esta quedando asi, ya que es mi pirmera ves usando tailwind cree un archivo css general para yo ir poniendo mis propios estilos .