Concepto clave
La gestión de estado avanzada en aplicaciones Expo implica coordinar múltiples fuentes de datos y mantener la consistencia en aplicaciones complejas. Piensa en esto como dirigir una orquesta sinfónica: cada instrumento (componente) debe tocar en armonía, siguiendo la partitura (estado global), mientras el director (gestor de estado) asegura que todos estén sincronizados. En aplicaciones reales, esto significa manejar datos del servidor, caché local, preferencias del usuario y estado de UI simultáneamente.
La arquitectura escalable se refiere a diseñar tu aplicación de manera que pueda crecer sin volverse inmanejable. Imagina construir un rascacielos: necesitas cimientos sólidos (patrones de arquitectura), sistemas modulares (separación de responsabilidades) y planes de expansión (rutas de actualización claras). En Expo, esto se traduce en estructurar tu proyecto para que nuevos desarrolladores puedan entenderlo rápidamente y nuevas funcionalidades puedan añadirse sin romper lo existente.
Cómo funciona en la práctica
Vamos a implementar un sistema de gestión de estado para una aplicación de comercio electrónico con Expo. Primero, definimos nuestras necesidades: productos (datos del servidor), carrito (estado local persistente), usuario (autenticación) y preferencias (configuración). Usaremos Redux Toolkit para el estado global y Context API para estados más específicos.
Paso 1: Configura la estructura de carpetas. Crea src/store/ para Redux, src/contexts/ para Context API, y src/hooks/ para hooks personalizados. Paso 2: Define los slices de Redux para productos y carrito. Paso 3: Crea contextos para autenticación y tema. Paso 4: Implementa hooks personalizados para acceder fácilmente a estos estados desde cualquier componente.
Código en acción
Antes: Estado desorganizado en componentes individuales
// ProductScreen.js - ANTES import React, { useState, useEffect } from 'react'; import { View, Text } from 'react-native'; export default function ProductScreen({ route }) { const [product, setProduct] = useState(null); const [loading, setLoading] = useState(true); const [cart, setCart] = useState([]); useEffect(() => { fetchProduct(route.params.id).then(data => { setProduct(data); setLoading(false); }); }, []); const addToCart = () => { setCart([...cart, product]); // Problema: este estado no persiste entre pantallas }; if (loading) return Cargando...; return ( {product.name}); }
Después: Estado centralizado con Redux Toolkit
// store/cartSlice.js
import { createSlice, createAsyncThunk } from '@reduxjs/toolkit';
import AsyncStorage from '@react-native-async-storage/async-storage';
export const fetchCart = createAsyncThunk(
'cart/fetchCart',
async () => {
const cartData = await AsyncStorage.getItem('cart');
return cartData ? JSON.parse(cartData) : [];
}
);
const cartSlice = createSlice({
name: 'cart',
initialState: {
items: [],
status: 'idle',
},
reducers: {
addItem: (state, action) => {
state.items.push(action.payload);
AsyncStorage.setItem('cart', JSON.stringify(state.items));
},
removeItem: (state, action) => {
state.items = state.items.filter(item => item.id !== action.payload);
AsyncStorage.setItem('cart', JSON.stringify(state.items));
},
},
extraReducers: (builder) => {
builder
.addCase(fetchCart.pending, (state) => {
state.status = 'loading';
})
.addCase(fetchCart.fulfilled, (state, action) => {
state.status = 'succeeded';
state.items = action.payload;
});
},
});
export const { addItem, removeItem } = cartSlice.actions;
export default cartSlice.reducer;Errores comunes
- SOBRECARGA DE REDUX: Usar Redux para todo, incluso estado local de componentes. Solución: Usa
useStatepara estado que solo afecta a un componente, Context para estado compartido en un árbol pequeño, y Redux solo para estado verdaderamente global. - MUTACION DIRECTA DEL ESTADO: Modificar arrays u objetos directamente en Redux. Solución: Siempre usa inmutabilidad, con spread operators o funciones como
map/filter. - FALTA DE NORMALIZACION: Almacenar datos anidados que causan duplicación. Solución: Normaliza tus datos como lo harías en una base de datos, usando IDs como referencias.
- IGNORAR EL RENDIMIENTO: No memoizar selectores en componentes que se rerenderizan frecuentemente. Solución: Usa
createSelectorde Redux Toolkit ouseMemopara cálculos costosos. - ACOPLAMIENTO EXCESIVO: Componentes que conocen demasiado sobre la estructura del estado. Solución: Usa hooks personalizados como abstracción entre componentes y lógica de estado.
Checklist de dominio
- ¿Puedes explicar la diferencia entre estado local, contexto y estado global?
- ¿Implementaste persistencia de estado con AsyncStorage o similar?
- ¿Normalizaste datos complejos para evitar duplicación?
- ¿Usaste selectores memoizados para optimizar rendimiento?
- ¿Estructuraste tu store de Redux con slices separados por dominio?
- ¿Creaste hooks personalizados para abstraer la lógica de estado?
- ¿Documentaste las decisiones de arquitectura para tu equipo?
Refactorización de una aplicación Expo con gestión de estado avanzada
Refactoriza una aplicación existente de lista de tareas para usar una arquitectura escalable. La aplicación actual tiene todo el estado en componentes individuales y no escala bien.
- Analiza la aplicación base: Descarga el proyecto inicial desde el repositorio proporcionado. Identifica todos los lugares donde se maneja estado.
- Diseña la arquitectura: Crea un diagrama que muestre cómo organizarás el estado. Decide qué va a Redux (estado global), qué a Context API (estado compartido limitado), y qué se queda como estado local.
- Implementa Redux Toolkit: Configura el store con slices para tareas y filtros. Incluye persistencia con AsyncStorage.
- Crea contextos: Implementa un contexto para el tema (claro/oscuro) y otro para preferencias de usuario.
- Desarrolla hooks personalizados: Crea al menos 2 hooks como
useTasksyuseThemeque abstraigan el acceso al estado. - Refactoriza componentes: Actualiza todos los componentes para usar la nueva arquitectura, manteniendo la misma funcionalidad.
- Prueba la escalabilidad: Añade una nueva funcionalidad (como categorías de tareas) demostrando cómo se integra fácilmente en tu arquitectura.
- Comienza por identificar qué datos son verdaderamente globales vs locales
- Usa Redux DevTools para depurar el flujo de estado durante la refactorización
- Considera usar normalización para las tareas si tienen relaciones complejas
Evalua tu comprension
Completa el quiz interactivo de arriba para ganar XP.