Cómo crear un carrito de compras en Next.js con contexto y useReducer

Cómo crear un carrito de compras en Next.js con contexto y useReducer

Un carrito de compras es una parte esencial de cualquier sitio de comercio electrónico. Permite a los clientes almacenar y comprar productos.

En una aplicación de comercio electrónico Next.js, puede usar la API de contexto y el enlace useReducer para crear un carrito. La API de contexto simplifica el intercambio de datos del carrito entre componentes, mientras que useReducer maneja el estado del carrito.

Creación de la página del producto

En la carpeta de páginas, cree un nuevo archivo llamado Product.jsx que represente un solo producto.

export default function Product({id, name, price}) {
  return (
    <div>
      <p>{name}</p>
      <p>{price}</p>
      <button>Add to Cart</button>
    </div>
  )
}

El componente de producto acepta el ID, el nombre y el precio de un producto y lo muestra. También tiene un botón «Agregar al carrito».

Cuando un producto ya se agregó al carrito, el botón debe cambiar a un botón «eliminar del carrito» y si un producto no está en el carrito, la página debe mostrar el botón «Agregar al carrito».

Para implementar esta funcionalidad, deberá realizar un seguimiento de los artículos en el carrito utilizando la API de contexto y el enlace useReducer.

Crear un carrito de compras usando la API de contexto

La API de contexto le permite compartir datos entre diferentes componentes sin tener que pasar accesorios manualmente de padre a hijo. Estos componentes pueden ser la barra de navegación, la página de detalles del producto o la página de pago.

Cree un nuevo archivo llamado cartContext.js en una carpeta llamada contexto y cree el contexto.

import { createContext } from "react";

export const CartContext = createContext({
    items: [],
});

CartContext toma una matriz de elementos como valor predeterminado.

A continuación, cree el proveedor de contexto. Un proveedor de contexto permite que los componentes que consumen el contexto se suscriban a los cambios de contexto.

En una nueva función llamada cartProvider, agregue lo siguiente:

export const CartProvider = ({ children }) => {
  return <CartContext.Provider>{children}</CartContext.Provider>;
};

Para realizar un seguimiento de los artículos en el carrito, utilizará el enlace useReducer.

El gancho useReducer funciona como el gancho useState excepto que ayuda a administrar una lógica de estado más compleja. Acepta una función reductora y el estado inicial. Devuelve el estado actual y una función de despacho que pasa una acción a la función reductora.

Cree una nueva función llamada CartReducer y agregue el reductor.

const cartReducer = (state, action) => {
  const { type, payload } = action;

  switch (type) {
    case "ADD":
      return {
        ...state,
        items: payload.items,
      };

    case "REMOVE":
      return {
        ...state,
        items: payload.items,
      };

    default:
      throw new Error("No case for that type");
  }
};

La función reducer comprende una declaración de cambio que actualiza el estado según el tipo de acción. La función de reducción de carrito tiene acciones de «AGREGAR» y «ELIMINAR» que agregan al carrito y eliminan del carrito respectivamente.

Después de crear la función reducer, utilícela en el gancho useReducer. Comience creando la función CartProvider. Esta es la función que proporcionará el contexto a otros componentes.

export const CartProvider = ({children}) => {
  return <CartContext.Provider>{children}</CartContext.Provider>;
}

Luego, crea el enlace useReducer.

export const CartProvider = ({ children }) => {
  const [state, dispatch] = useReducer(cartReducer, { items: [] });
  return <CartContext.Provider>{children}</CartContext.Provider>;
};

La función de envío es responsable de actualizar el estado del carrito, así que modifique la función CartProvider para incluir funciones que envíen productos al gancho useReducer cuando se actualice el carrito.

import { createContext, useReducer } from "react";

export const CartProvider = ({ children }) => {
  const [state, dispatch] = useReducer(cartReducer, initialState);

  const addToCart = (product) => {
    const updatedCart = [...state.items, product];

    dispatch({
      type: "ADD",
      payload: {
        items: updatedCart,
      },
    });
  };

  const removeFromCart = (id) => {
    const updatedCart = state.items.filter(
      (currentProduct) => currentProduct.id! == id
    );

    dispatch({
      type: "REMOVE",
      payload: {
        items: updatedCart,
      },
    });
  };

  return <CartContext.Provider>{children}</CartContext.Provider>;
};

La función addToCart agrega el nuevo producto a los productos existentes y devuelve los productos actualizados en el objeto de carga útil de la función de despacho. Asimismo, la función removeFromCart filtra el artículo por ID y devuelve la lista actualizada.

También debe devolver la propiedad de valor en el proveedor CartContext.

export const CartProvider = ({ children }) => {
  const [state, dispatch] = useReducer(cartReducer, {
    items: [],
  });

  const addToCart = (product) => {};
  const removeFromCart = (id) => {};

  const value = {
    items: state.items,
    addToCart,
    removeFromCart,
  };

  return <CartContext.Provider value={value}>{children}</CartContext.Provider>;
}

La propuesta de valor se consume a través del gancho useContext.

Consumo del contexto del carrito

Hasta ahora ha creado el contexto del carrito y ha creado una función useReducer que actualiza el carrito. A continuación, consumirá el contexto del carrito en el componente del producto mediante el gancho useContext.

Comience por envolver index.js, el componente superior, con el proveedor de contexto para que los valores de contexto estén disponibles en toda la aplicación.

import { CartProvider } from "../context/cartContext";

function MyApp({ Component, pageProps }) {
  return (
    <CartProvider>
      <Component {...pageProps} />
    </CartProvider>
  );
}


export default MyApp;

Luego importe el enlace useContext y el proveedor de contexto del carrito en Product.js

import { useContext } from "react"
import { CartContext } from "../context/cartContext"

export default function Product() {
  const {items, addToCart, removeFromCart} = useContext(CartContext)


  return (
    <>
      <div>
        <p>{name}</p>
        <p>{price}</p>
        <button>Add to Cart</button>
      </div>
    </>
  )
}

La función del botón depende de si el artículo ya está en el carrito. Si existe un artículo en el carrito, el botón debe eliminarlo del carrito y si un artículo aún no está en el carrito, debe agregarlo. Esto significa que debe realizar un seguimiento del estado del elemento mediante useEffect y useState. El código useEffect verifica si el artículo está en el carrito después de que el componente se procesa mientras useState actualiza el estado del artículo.

const [exists, setExists] = useState(false);

useEffect(() => {
  const inCart = items.find((item) => item.id === id);


  if (inCart) {
      setExists(true);
  } else {
      setExists(false);
  }
}, [items, id]);

Ahora, use la representación condicional para mostrar el botón en función del estado existente.

return (
  <div>
    <p>{name}</p>
    <p>{price}</p>
    {
     exists
     ? <button onClick={() => removeFromCart(id)}>Remove from Cart</button>
     : <button onClick={() => addToCart({id, name, price})}>Add to Cart</button>
   }
  </div>
)

Tenga en cuenta que las funciones del controlador onClick son las funciones removeFromCart y addToCart definidas en el proveedor de contexto.

Agregar más funcionalidad al carrito

Aprendió a crear un carrito de compras usando la API de contexto y el enlace useReducer.

Aunque esta guía solo cubrió la funcionalidad de agregar y quitar, puede usar los mismos conceptos para agregar más funciones, como ajustar las cantidades de artículos del carrito. Lo crucial es comprender la API de contexto y cómo usar ganchos para actualizar los detalles del carrito.

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *