Concepto clave
Planificar y estructurar un proyecto de productividad con Expo es como diseñar los planos de un edificio antes de construirlo. No puedes simplemente empezar a colocar ladrillos; necesitas entender el terreno, los materiales disponibles y cómo se conectará cada habitación. En desarrollo de aplicaciones, esto significa definir claramente los objetivos del proyecto, las funcionalidades clave y cómo se organizará el código para mantenerlo escalable y mantenible.
La planificación efectiva involucra tres pilares: arquitectura de carpetas (cómo organizas los archivos), gestión de estado (cómo manejarás los datos de la aplicación) y definición de componentes (qué partes reutilizables construirás). Una analogía del mundo real sería organizar una cocina profesional: tienes zonas específicas para preparación (componentes), almacenamiento (estado) y flujo de trabajo (estructura de carpetas), todo diseñado para eficiencia y evitar caos durante el servicio.
Cómo funciona en la práctica
Vamos a planificar una aplicación de productividad llamada "TaskFlow" que permitirá a los usuarios gestionar tareas, establecer recordatorios y ver estadísticas. Primero, definimos los requisitos:
- Pantalla principal con lista de tareas (pendientes/completadas)
- Formulario para crear/editar tareas con título, descripción, fecha límite y prioridad
- Notificaciones push para recordatorios
- Estadísticas semanales de productividad
- Autenticación de usuarios
Paso a paso, comenzamos con la estructura de carpetas. Creamos un proyecto Expo con npx create-expo-app TaskFlow, luego organizamos:
TaskFlow/
├── app/
│ ├── (tabs)/
│ │ ├── index.tsx
│ │ ├── tasks.tsx
│ │ └── stats.tsx
│ ├── auth/
│ │ ├── login.tsx
│ │ └── register.tsx
│ └── _layout.tsx
├── components/
│ ├── TaskCard.tsx
│ ├── PriorityBadge.tsx
│ └── DatePicker.tsx
├── lib/
│ ├── api.ts
│ └── notifications.ts
├── store/
│ ├── tasksSlice.ts
│ └── authSlice.ts
└── assets/
├── images/
└── fonts/Esta estructura separa claramente las pantallas (app/), componentes reutilizables, lógica de negocio (lib/) y estado global (store/), siguiendo principios de separación de responsabilidades.
Codigo en accion
Veamos un ejemplo de cómo definiríamos el slice de Redux para gestionar tareas, usando Redux Toolkit. Primero, instalamos las dependencias: npm install @reduxjs/toolkit react-redux. Luego, creamos el store:
// store/tasksSlice.ts
import { createSlice, PayloadAction } from '@reduxjs/toolkit';
interface Task {
id: string;
title: string;
description: string;
dueDate: string;
priority: 'low' | 'medium' | 'high';
completed: boolean;
}
interface TasksState {
tasks: Task[];
loading: boolean;
error: string | null;
}
const initialState: TasksState = {
tasks: [],
loading: false,
error: null,
};
const tasksSlice = createSlice({
name: 'tasks',
initialState,
reducers: {
addTask: (state, action: PayloadAction) => {
state.tasks.push(action.payload);
},
toggleTask: (state, action: PayloadAction) => {
const task = state.tasks.find(t => t.id === action.payload);
if (task) {
task.completed = !task.completed;
}
},
deleteTask: (state, action: PayloadAction) => {
state.tasks = state.tasks.filter(t => t.id !== action.payload);
},
setLoading: (state, action: PayloadAction) => {
state.loading = action.payload;
},
},
});
export const { addTask, toggleTask, deleteTask, setLoading } = tasksSlice.actions;
export default tasksSlice.reducer;Ahora, mejoramos este código para manejar operaciones asíncronas, como cargar tareas desde una API. Refactorizamos usando createAsyncThunk:
// store/tasksSlice.ts - Versión mejorada
import { createSlice, createAsyncThunk, PayloadAction } from '@reduxjs/toolkit';
import { fetchTasksFromAPI } from '../lib/api';
// Thunk para cargar tareas
export const loadTasks = createAsyncThunk(
'tasks/loadTasks',
async (userId: string) => {
const response = await fetchTasksFromAPI(userId);
return response.data;
}
);
const tasksSlice = createSlice({
name: 'tasks',
initialState,
reducers: {
// reducers sincrónicos aquí
},
extraReducers: (builder) => {
builder
.addCase(loadTasks.pending, (state) => {
state.loading = true;
state.error = null;
})
.addCase(loadTasks.fulfilled, (state, action) => {
state.tasks = action.payload;
state.loading = false;
})
.addCase(loadTasks.rejected, (state, action) => {
state.loading = false;
state.error = action.error.message || 'Error al cargar tareas';
});
},
});Este enfoque maneja estados de carga y error de forma elegante, mostrando el "antes y después" de una implementación básica a una robusta.
Errores comunes
- Estructura plana de carpetas: Poner todos los archivos en una sola carpeta lleva a caos en proyectos grandes. Solución: Usa una jerarquía clara como la mostrada, agrupando por funcionalidad.
- Estado local excesivo: Manejar todo el estado en componentes individuales dificulta el compartir datos. Solución: Centraliza el estado con Redux o Context API para datos globales como tareas y autenticación.
- Falta de tipado: No usar TypeScript en proyectos avanzados aumenta errores en tiempo de ejecución. Solución: Configura TypeScript desde el inicio y define interfaces para todos los datos.
- Ignorar el rendimiento: Renderizar listas largas sin virtualización o usar efectos innecesarios. Solución: Usa
FlatListde React Native para listas y memoiza componentes conReact.memo. - Planificación insuficiente de notificaciones: Dejar las notificaciones push para el final causa problemas de permisos. Solución: Integra
expo-notificationsal inicio y prueba en dispositivos reales.
Checklist de dominio
- ¿Definiste al menos 5 requisitos funcionales claros para la aplicación?
- ¿Estructuraste las carpetas separando pantallas, componentes, lógica y estado?
- ¿Implementaste un sistema de gestión de estado (ej., Redux) con slices para tareas y autenticación?
- ¿Creaste interfaces TypeScript para todos los modelos de datos principales?
- ¿Planificaste la integración de notificaciones push desde el diseño inicial?
- ¿Diseñaste al menos 3 componentes reutilizables (ej., TaskCard, PriorityBadge)?
- ¿Configuraste un flujo de navegación con Expo Router que incluya tabs y stacks?
Diseña y estructura un proyecto de productividad con Expo
En este ejercicio, aplicarás los conceptos de planificación para crear la base de una aplicación de productividad. Sigue estos pasos:
- Define los requisitos: Escribe una lista de al menos 5 funcionalidades clave para una app de gestión de tareas, incluyendo autenticación, notificaciones y estadísticas.
- Crea la estructura de carpetas: Usando la terminal, genera un nuevo proyecto Expo con
npx create-expo-app MyProductivityApp. Luego, organiza las carpetas según la arquitectura mostrada en la lección (app/, components/, etc.). Asegúrate de incluir subcarpetas para tabs y auth dentro de app/. - Configura TypeScript y dependencias: Instala Redux Toolkit y React Redux con
npm install @reduxjs/toolkit react-redux. Configura TypeScript si no está habilitado por defecto. - Implementa un slice de Redux: Crea un archivo
store/tasksSlice.tsque defina el estado para tareas, incluyendo acciones para agregar, completar y eliminar tareas. Usa interfaces TypeScript para el modelo Task. - Diseña un componente reutilizable: Crea
components/TaskCard.tsxque muestre el título, prioridad y estado de una tarea, recibiendo props tipadas. - Planifica la navegación: En
app/_layout.tsx, configura un stack de navegación con Expo Router que incluya pantallas de login y tabs para tareas y estadísticas.
Entrega un resumen en formato markdown con capturas de pantalla de tu estructura de carpetas y fragmentos de código clave.
Pistas- Usa
expo-routerpara la navegación; sigue la documentación oficial de Expo para configurar tabs. - En el slice de Redux, define prioridades como unión de tipos literales (ej., 'low' | 'medium' | 'high') para mayor seguridad.
- Para el componente TaskCard, considera usar
TouchableOpacitypara hacerlo interactivo y pasar un callback para manejar toques.
Evalua tu comprension
Completa el quiz interactivo de arriba para ganar XP.