Concepto clave
En el desarrollo de aplicaciones con Expo, la interfaz y la lógica deben evolucionar juntas como un sistema simbiótico. Piensa en una aplicación de productividad como un taller de carpintería: la interfaz son las herramientas visibles (martillos, sierras) que el usuario maneja directamente, mientras que la lógica es el conocimiento del carpintero sobre cómo usar cada herramienta para transformar madera en muebles. Ambas partes deben estar perfectamente sincronizadas para crear valor.
La arquitectura moderna en Expo se basa en componentes que encapsulan tanto la presentación como el comportamiento. Esto es similar a cómo un smartphone integra hardware y software: la pantalla táctil (interfaz) responde inmediatamente a los gestos porque el sistema operativo (lógica) procesa cada entrada en milisegundos. En nuestro contexto, esto significa diseñar componentes que no solo se vean bien, sino que también manejen estados complejos, efectos secundarios y optimizaciones de rendimiento de manera eficiente.
Cómo funciona en la práctica
Vamos a construir la pantalla principal de nuestra aplicación de productividad. Esta pantalla mostrará una lista de tareas con funcionalidades de arrastrar y soltar para reordenar, marcado como completado, y filtrado por categorías. Sigue estos pasos:
- Crea un componente
TaskListque reciba un array de tareas como prop - Implementa un estado local para manejar el orden de las tareas usando el hook
useState - Integra la librería
react-native-gesture-handlerpara habilitar gestos de arrastre - Conecta cada tarea con funciones de lógica de negocio: completar, eliminar, editar
- Agrega un sistema de filtros que actualice la lista en tiempo real
La clave está en mantener la lógica separada pero accesible. Por ejemplo, la función que marca una tarea como completada debe actualizar tanto el estado local como (potencialmente) una base de datos remota, sin bloquear la interfaz de usuario.
Codigo en accion
Aquí tienes un ejemplo funcional del componente principal. Primero, la versión inicial básica:
import React, { useState } from 'react';
import { View, FlatList, Text } from 'react-native';
export default function TaskList({ tasks }) {
const [taskItems, setTaskItems] = useState(tasks);
const toggleComplete = (id) => {
setTaskItems(prev => prev.map(task =>
task.id === id ? { ...task, completed: !task.completed } : task
));
};
return (
item.id}
renderItem={({ item }) => (
{item.title}
toggleComplete(item.id)}>
{item.completed ? '✅' : '⬜'}
)}
/>
);
}Ahora, la versión mejorada con arrastre y soltar, y optimizaciones:
import React, { useState, useCallback } from 'react';
import { View, FlatList, Text, TouchableOpacity } from 'react-native';
import DraggableFlatList from 'react-native-draggable-flatlist';
export default function EnhancedTaskList({ initialTasks }) {
const [tasks, setTasks] = useState(initialTasks);
const [filter, setFilter] = useState('all');
const filteredTasks = tasks.filter(task => {
if (filter === 'completed') return task.completed;
if (filter === 'pending') return !task.completed;
return true;
});
const toggleComplete = useCallback((id) => {
setTasks(prev => prev.map(task =>
task.id === id ? { ...task, completed: !task.completed } : task
));
}, []);
const reorderTasks = useCallback(({ data }) => {
setTasks(data);
}, []);
return (
setFilter('all')}>
Todas
setFilter('pending')}>
Pendientes
setFilter('completed')}>
Completadas
item.id}
onDragEnd={reorderTasks}
renderItem={({ item, drag }) => (
{item.title}
toggleComplete(item.id)}>
{item.completed ? '✅' : '⬜'}
)}
/>
);
}Errores comunes
- Acoplamiento excesivo entre vista y lógica: Mezclar la lógica de negocio directamente en los componentes de presentación. Solución: Extraer la lógica a hooks personalizados o servicios separados.
- Re-renders innecesarios: No usar
useCallbackouseMemopara funciones y valores estables, causando que componentes hijos se re-rendericen sin necesidad. Solución: Memorizar callbacks y valores computados costosos. - Manejo ineficiente del estado: Actualizar el estado completo cuando solo cambia una propiedad. Solución: Usar actualizaciones inmutables precisas con el spread operator.
- Ignorar la experiencia de usuario durante operaciones asíncronas: No mostrar indicadores de carga o estados de error. Solución: Implementar estados de carga, error y éxito en cada operación asíncrona.
- Optimización prematura: Agregar complejidad de rendimiento antes de identificar cuellos de botella reales. Solución: Perfilar la aplicación primero, luego optimizar solo los componentes problemáticos.
Checklist de dominio
- ¿Puedes construir un componente que maneje tanto la interfaz como la lógica de negocio sin acoplamiento excesivo?
- ¿Implementas gestos complejos (arrastrar, deslizar) manteniendo el rendimiento fluido?
- ¿Utilizas hooks de React (useState, useEffect, useCallback, useMemo) de manera óptima para evitar re-renders?
- ¿Manejas estados asíncronos (carga, error, éxito) en todas las operaciones de red?
- ¿Estructuras tu código para que la lógica pueda ser testeada independientemente de la interfaz?
- ¿Implementas optimizaciones de rendimiento basadas en datos reales de profiling?
- ¿Diseñas interfaces que responden inmediatamente a las acciones del usuario, sin retrasos perceptibles?
Implementa un sistema de tareas con arrastre, filtrado y sincronización en tiempo real
En este ejercicio, construirás la funcionalidad completa de la pantalla de tareas para nuestra aplicación de productividad. Sigue estos pasos:
- Crea un nuevo componente
TaskManagerScreenen tu proyecto Expo - Implementa un estado inicial con al menos 5 tareas, cada una con propiedades: id, title, description, completed, category, priority
- Agrega la capacidad de reordenar tareas mediante arrastre y soltar usando
react-native-draggable-flatlist - Implementa tres filtros: Todas, Pendientes, Completadas que actualicen la lista inmediatamente
- Crea un sistema de sincronización simulada que "guarde" los cambios cada 2 segundos (usa
setTimeoutpara simular una API) - Agrega indicadores visuales para mostrar cuando se está sincronizando y cuando hay errores
- Optimiza el componente para evitar re-renders innecesarios cuando se cambian los filtros
El resultado final debe ser una pantalla completamente funcional donde los usuarios puedan organizar sus tareas, filtrarlas y ver cómo se sincronizan en segundo plano.
Pistas- Usa useCallback para las funciones que pasas como props a componentes hijos, especialmente las que actualizan estado
- Considera usar un contexto de React para manejar el estado global de las tareas si planeas compartirlo entre múltiples pantallas
- Para la sincronización simulada, crea una función que copie el estado actual, espere 2 segundos y luego actualice un estado de 'lastSynced'
Evalua tu comprension
Completa el quiz interactivo de arriba para ganar XP.