Concepto clave
En el desarrollo de aplicaciones de comercio electrónico con React Native y Expo, el concepto central es la arquitectura de componentes reutilizables que manejan estados complejos. Piensa en una tienda física: tienes diferentes secciones (productos, carrito, checkout) que funcionan de manera independiente pero se comunican entre sí. En React Native, cada pantalla es un componente que gestiona su propio estado local, mientras que herramientas como Context API o Redux actúan como el "gerente de tienda" que coordina información global como el carrito de compras.
La clave está en separar la lógica de negocio de la interfaz de usuario. Imagina que estás construyendo un mostrador de pago: la pantalla muestra los productos (UI), pero el cálculo de impuestos y descuentos (lógica) debe estar en funciones separadas. Esto no solo hace el código más mantenible, sino que también facilita las pruebas y futuras actualizaciones.
Cómo funciona en la práctica
Vamos a implementar un carrito de compras paso a paso. Primero, crea un contexto para manejar el estado global del carrito. Luego, desarrolla componentes específicos: ProductCard para mostrar items, CartScreen para listar productos seleccionados, y CheckoutButton para procesar la compra. Cada componente debe recibir solo los datos que necesita, evitando acoplamientos innecesarios.
Paso 1: Configura el contexto del carrito con useReducer para manejar acciones como agregar, eliminar o actualizar cantidades. Paso 2: Crea un servicio API que se comunique con tu backend para verificar inventario y precios en tiempo real. Paso 3: Implementa navegación entre pantallas usando React Navigation, asegurando una experiencia fluida. Paso 4: Añade persistencia local con AsyncStorage para que el carrito sobreviva a cierres de la app.
Código en acción
Aquí tienes un ejemplo funcional del contexto del carrito:
// CartContext.js
import React, { createContext, useReducer, useContext } from 'react';
const CartContext = createContext();
const cartReducer = (state, action) => {
switch (action.type) {
case 'ADD_ITEM':
const existingItem = state.items.find(item => item.id === action.payload.id);
if (existingItem) {
return {
...state,
items: state.items.map(item =>
item.id === action.payload.id
? { ...item, quantity: item.quantity + 1 }
: item
),
};
}
return { ...state, items: [...state.items, { ...action.payload, quantity: 1 }] };
case 'REMOVE_ITEM':
return {
...state,
items: state.items.filter(item => item.id !== action.payload),
};
case 'CLEAR_CART':
return { ...state, items: [] };
default:
return state;
}
};
export const CartProvider = ({ children }) => {
const [state, dispatch] = useReducer(cartReducer, { items: [] });
return (
{children}
);
};
export const useCart = () => useContext(CartContext);Y así se usa en un componente de producto:
// ProductCard.js import React from 'react'; import { View, Text, Button } from 'react-native'; import { useCart } from './CartContext'; const ProductCard = ({ product }) => { const { dispatch } = useCart(); const handleAddToCart = () => { dispatch({ type: 'ADD_ITEM', payload: product }); }; return ( {product.name} Precio: ${product.price}); }; export default ProductCard;
Errores comunes
- Manejo ineficiente del estado global: Usar solo useState en componentes padres y pasar props en cadena (prop drilling). Solución: Implementa Context API o Redux para estados compartidos.
- Falta de validación en el carrito: Permitir cantidades negativas o agregar productos sin stock. Solución: Añade verificaciones en el reducer y sincroniza con el backend.
- Navegación desorganizada: Crear stacks de navegación anidados sin una estructura clara. Solución: Planifica rutas principales (Home, Productos, Carrito, Perfil) y usa navegación anidada solo cuando sea necesario.
- Olvidar la persistencia: El carrito se vacía al cerrar la app. Solución: Integra AsyncStorage para guardar y recuperar el estado.
- No optimizar imágenes: Usar imágenes de alta resolución sin comprimir, ralentizando la app. Solución: Usa servicios como Cloudinary o comprime assets localmente.
Checklist de dominio
- ¿Implementaste un contexto global para manejar el estado del carrito?
- ¿Tus componentes son reutilizables y reciben solo los props necesarios?
- ¿Validaste datos como precios y stock antes de procesar compras?
- ¿Configuraste navegación fluida entre pantallas de productos, carrito y checkout?
- ¿Añadiste persistencia local para mantener el carrito entre sesiones?
- ¿Optimizaste imágenes y assets para rendimiento en dispositivos móviles?
- ¿Probaste la app en ambos emuladores (iOS y Android) para asegurar compatibilidad?
Refactoriza el carrito de compras para incluir descuentos
En este ejercicio, mejorarás la lógica del carrito para aplicar descuentos basados en reglas de negocio. Sigue estos pasos:
- Abre tu archivo CartContext.js y modifica el reducer para manejar un nuevo tipo de acción: 'APPLY_DISCOUNT'.
- Crea una función calculateTotal que sume los precios de los items, aplique descuentos (por ejemplo, 10% si el total supera $100) y calcule impuestos (supón 16% IVA).
- Actualiza el estado para incluir total, descuento e impuestos como propiedades separadas.
- En el componente CartScreen, muestra el desglose: subtotal, descuento, impuestos y total final.
- Añade un botón que permita aplicar un código de descuento (puede simularse con un texto fijo por ahora).
Entrega el código del reducer actualizado y el componente CartScreen.
Pistas- Usa un switch case adicional en el reducer para 'APPLY_DISCOUNT'
- Considera almacenar el porcentaje de descuento en el estado
- Recuerda recalcular el total cada vez que cambien items o descuentos
Evalua tu comprension
Completa el quiz interactivo de arriba para ganar XP.