Tutorial: Aplicación React Native para Agregar Tareas - Minimalista

 Voy a crear una aplicación React Native simple que permite agregar tareas a una lista usando un formulario con un solo input. Será minimalista y bien explicado paso a paso.

Estructura del Proyecto

text
react-native-todo-app/
├── src/
│   ├── components/
│   │   ├── TaskForm.js
│   │   └── TaskList.js
│   └── App.js
├── package.json
└── README.md

Paso a Paso

1. Crear el proyecto React Native

bash
npx react-native init ReactNativeTodoApp
cd ReactNativeTodoApp

2. Crear el componente App.js

jsx
import React, { useState } from 'react';
import { View, Text, StyleSheet, SafeAreaView } from 'react-native';
import TaskForm from './src/components/TaskForm';
import TaskList from './src/components/TaskList';

function App() {
  // Estado para almacenar la lista de tareas
  const [tasks, setTasks] = useState([]);

  // Función para agregar una nueva tarea
  const addTask = (taskText) => {
    // Crear un objeto tarea con texto y un ID único
    const newTask = {
      id: Date.now(), // Usamos timestamp como ID único
      text: taskText,
      completed: false
    };
    
    // Agregar la nueva tarea al estado
    setTasks([...tasks, newTask]);
  };

  return (
    <SafeAreaView style={styles.container}>
      <View style={styles.app}>
        <Text style={styles.title}>Lista de Tareas</Text>
        <TaskForm onAddTask={addTask} />
        <TaskList tasks={tasks} />
      </View>
    </SafeAreaView>
  );
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: '#f5f5f5'
  },
  app: {
    flex: 1,
    maxWidth: 500,
    marginHorizontal: 'auto',
    padding: 20,
    width: '100%'
  },
  title: {
    fontSize: 24,
    fontWeight: 'bold',
    textAlign: 'center',
    color: '#333',
    marginBottom: 20
  }
});

export default App;

3. Crear el componente TaskForm.js

jsx
import React, { useState } from 'react';
import { View, TextInput, TouchableOpacity, Text, StyleSheet, Alert } from 'react-native';

function TaskForm({ onAddTask }) {
  // Estado para el valor del input
  const [taskText, setTaskText] = useState('');

  // Manejar el cambio en el input
  const handleChange = (text) => {
    setTaskText(text);
  };

  // Manejar el envío del formulario
  const handleSubmit = () => {
    // Validar que el input no esté vacío
    if (taskText.trim() === '') {
      Alert.alert('Error', 'Por favor, escribe una tarea');
      return;
    }
    
    // Llamar a la función para agregar la tarea
    onAddTask(taskText);
    
    // Limpiar el input después de agregar
    setTaskText('');
  };

  return (
    <View style={styles.formContainer}>
      <View style={styles.formRow}>
        <TextInput
          style={styles.input}
          value={taskText}
          onChangeText={handleChange}
          placeholder="Escribe una nueva tarea..."
          placeholderTextColor="#888"
        />
        <TouchableOpacity style={styles.button} onPress={handleSubmit}>
          <Text style={styles.buttonText}>Agregar</Text>
        </TouchableOpacity>
      </View>
    </View>
  );
}

const styles = StyleSheet.create({
  formContainer: {
    marginBottom: 20
  },
  formRow: {
    flexDirection: 'row'
  },
  input: {
    flex: 1,
    padding: 12,
    fontSize: 16,
    borderWidth: 1,
    borderColor: '#ddd',
    borderTopLeftRadius: 4,
    borderBottomLeftRadius: 4,
    backgroundColor: 'white'
  },
  button: {
    paddingHorizontal: 20,
    backgroundColor: '#4CAF50',
    justifyContent: 'center',
    alignItems: 'center',
    borderTopRightRadius: 4,
    borderBottomRightRadius: 4
  },
  buttonText: {
    color: 'white',
    fontSize: 16,
    fontWeight: '500'
  }
});

export default TaskForm;

4. Crear el componente TaskList.js

jsx
import React from 'react';
import { View, Text, FlatList, StyleSheet } from 'react-native';

function TaskList({ tasks }) {
  // Verificar si no hay tareas
  if (tasks.length === 0) {
    return (
      <View style={styles.emptyContainer}>
        <Text style={styles.emptyText}>No hay tareas aún. ¡Agrega una!</Text>
      </View>
    );
  }

  return (
    <View style={styles.listContainer}>
      <Text style={styles.subtitle}>Tareas ({tasks.length})</Text>
      <FlatList
        data={tasks}
        keyExtractor={(item) => item.id.toString()}
        renderItem={({ item }) => (
          <View style={styles.taskItem}>
            <Text style={styles.taskText}>{item.text}</Text>
          </View>
        )}
      />
    </View>
  );
}

const styles = StyleSheet.create({
  listContainer: {
    flex: 1,
    backgroundColor: '#f9f9f9',
    borderRadius: 4,
    padding: 15
  },
  subtitle: {
    fontSize: 18,
    fontWeight: '600',
    color: '#555',
    borderBottomWidth: 1,
    borderBottomColor: '#eee',
    paddingBottom: 10,
    marginBottom: 10
  },
  taskItem: {
    padding: 12,
    backgroundColor: 'white',
    marginBottom: 8,
    borderRadius: 4,
    borderLeftWidth: 4,
    borderLeftColor: '#4CAF50',
    shadowColor: '#000',
    shadowOffset: { width: 0, height: 2 },
    shadowOpacity: 0.05,
    shadowRadius: 4,
    elevation: 2
  },
  taskText: {
    fontSize: 16
  },
  emptyContainer: {
    padding: 20,
    alignItems: 'center'
  },
  emptyText: {
    textAlign: 'center',
    color: '#888',
    fontStyle: 'italic',
    fontSize: 16
  }
});

export default TaskList;

5. Explicación del Código

1. Componente App (Padre)

  • Usa useState para manejar el estado de las tareas

  • Pasa la función addTask como prop al componente TaskForm

  • Pasa el array tasks como prop al componente TaskList

  • Contenedor principal con estilos minimalistas

2. Componente TaskForm (Hijo)

  • Tiene su propio estado local (taskText) para manejar el input

  • Valida que el input no esté vacío antes de agregar la tarea

  • Llama a onAddTask (prop recibida) cuando se presiona el botón

  • Limpia el input después de agregar la tarea

  • Usa flexDirection: 'row' para diseño horizontal

3. Componente TaskList (Hijo)

  • Recibe tasks como prop

  • Muestra un mensaje si no hay tareas

  • Usa FlatList para renderizar cada tarea de manera eficiente

  • Cada tarea tiene un key único (ID) para optimización

  • Bordes y sombras sutiles para diferenciar elementos

4. Flujo de Datos

  1. El usuario escribe en el TextInput en TaskForm

  2. Al presionar "Agregar", TaskForm llama a addTask (pasada como prop desde App)

  3. addTask crea un nuevo objeto tarea y actualiza el estado en App

  4. El nuevo estado se pasa como prop a TaskList, que se re-renderiza con la nueva tarea

6. Ejecutar la Aplicación

bash
# Para iOS
npx react-native run-ios

# Para Android
npx react-native run-android

La aplicación estará disponible en el emulador o dispositivo físico.

7. Características del Proyecto

  • Simple: Solo un input para agregar tareas

  • Modular: Componentes separados con responsabilidades claras

  • Estado Centralizado: El estado principal está en el componente App

  • Props y Callbacks: Comunicación entre componentes mediante props

  • Estilo Mínimo: Estilos básicos pero funcionales con React Native StyleSheet

8. Posibles Mejoras

Si quieres expandir el proyecto, podrías agregar:

  • Botón para eliminar tareas

  • Checkbox para marcar tareas como completadas

  • Almacenamiento en AsyncStorage para persistencia

  • Filtros para mostrar tareas activas/completadas

  • Animaciones al agregar o eliminar tareas

Este proyecto es un excelente punto de partida para entender los conceptos fundamentales de React Native como componentes, estado, props y manejo de eventos en dispositivos móviles.

CSS Grid Necesario

En React Native, no tenemos CSS Grid como en web, pero utilizamos flexbox para el diseño. El código mantiene la estructura necesaria:

  1. flexDirection: 'row' en TaskForm para colocar el input y botón en horizontal

  2. flex: 1 en el input para que ocupe el espacio disponible

  3. Contenedores anidados para mantener la estructura jerárquica

  4. FlatList para renderizar listas de manera eficiente

La estructura visual se mantiene con márgenes, paddings y bordes, pero sin CSS adicional innecesario.

Comentarios

Entradas más populares de este blog

Guía Paso a Paso para Entender React Native (antes del Tutorial)

5. Vista Rapida: Estructura Projecto Base