Planificar y estructurar el proyecto de productividad

Lectura
15 min~6 min lectura

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:

  1. Pantalla principal con lista de tareas (pendientes/completadas)
  2. Formulario para crear/editar tareas con título, descripción, fecha límite y prioridad
  3. Notificaciones push para recordatorios
  4. Estadísticas semanales de productividad
  5. 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 FlatList de React Native para listas y memoiza componentes con React.memo.
  • Planificación insuficiente de notificaciones: Dejar las notificaciones push para el final causa problemas de permisos. Solución: Integra expo-notifications al inicio y prueba en dispositivos reales.

Checklist de dominio

  1. ¿Definiste al menos 5 requisitos funcionales claros para la aplicación?
  2. ¿Estructuraste las carpetas separando pantallas, componentes, lógica y estado?
  3. ¿Implementaste un sistema de gestión de estado (ej., Redux) con slices para tareas y autenticación?
  4. ¿Creaste interfaces TypeScript para todos los modelos de datos principales?
  5. ¿Planificaste la integración de notificaciones push desde el diseño inicial?
  6. ¿Diseñaste al menos 3 componentes reutilizables (ej., TaskCard, PriorityBadge)?
  7. ¿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:

  1. 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.
  2. 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/.
  3. 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.
  4. Implementa un slice de Redux: Crea un archivo store/tasksSlice.ts que defina el estado para tareas, incluyendo acciones para agregar, completar y eliminar tareas. Usa interfaces TypeScript para el modelo Task.
  5. Diseña un componente reutilizable: Crea components/TaskCard.tsx que muestre el título, prioridad y estado de una tarea, recibiendo props tipadas.
  6. 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-router para 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 TouchableOpacity para hacerlo interactivo y pasar un callback para manejar toques.

Evalua tu comprension

Completa el quiz interactivo de arriba para ganar XP.