En este proyecto, vamos a aplicar los conocimientos adquiridos en los módulos anteriores para construir una aplicación de lista de tareas (To-Do List) utilizando React Native. Este proyecto te permitirá consolidar conceptos clave como componentes, estado, ciclo de vida, manejo de eventos, y almacenamiento local.

Objetivos del Proyecto

  • Crear una interfaz de usuario para agregar, visualizar y eliminar tareas.
  • Manejar el estado de la aplicación para gestionar las tareas.
  • Almacenar las tareas localmente utilizando AsyncStorage.
  • Implementar un diseño responsivo y atractivo.

Estructura del Proyecto

  1. Configuración Inicial
  2. Creación de Componentes
  3. Manejo del Estado
  4. Persistencia de Datos con AsyncStorage
  5. Estilizado de la Aplicación
  6. Pruebas y Depuración

  1. Configuración Inicial

Crear un Nuevo Proyecto

Primero, crea un nuevo proyecto de React Native utilizando npx react-native init:

npx react-native init TodoListApp
cd TodoListApp

Instalar Dependencias Necesarias

Instalaremos @react-native-async-storage/async-storage para manejar el almacenamiento local:

npm install @react-native-async-storage/async-storage

  1. Creación de Componentes

Estructura de Archivos

Organiza tu proyecto creando una carpeta components para almacenar los componentes reutilizables:

TodoListApp/
├── components/
│   ├── TaskInput.js
│   ├── TaskItem.js
├── App.js

Componente TaskInput

Este componente manejará la entrada del usuario para agregar nuevas tareas.

// components/TaskInput.js
import React, { useState } from 'react';
import { View, TextInput, Button, StyleSheet } from 'react-native';

const TaskInput = ({ onAddTask }) => {
  const [task, setTask] = useState('');

  const handleAddTask = () => {
    if (task.trim()) {
      onAddTask(task);
      setTask('');
    }
  };

  return (
    <View style={styles.inputContainer}>
      <TextInput
        placeholder="Add a new task"
        style={styles.input}
        value={task}
        onChangeText={setTask}
      />
      <Button title="Add" onPress={handleAddTask} />
    </View>
  );
};

const styles = StyleSheet.create({
  inputContainer: {
    flexDirection: 'row',
    justifyContent: 'space-between',
    alignItems: 'center',
    marginBottom: 20,
  },
  input: {
    width: '80%',
    borderBottomWidth: 1,
    borderBottomColor: '#ccc',
    padding: 10,
  },
});

export default TaskInput;

Componente TaskItem

Este componente representará cada tarea en la lista.

// components/TaskItem.js
import React from 'react';
import { View, Text, Button, StyleSheet } from 'react-native';

const TaskItem = ({ task, onDelete }) => {
  return (
    <View style={styles.taskItem}>
      <Text>{task}</Text>
      <Button title="Delete" onPress={onDelete} />
    </View>
  );
};

const styles = StyleSheet.create({
  taskItem: {
    padding: 10,
    backgroundColor: '#f9f9f9',
    borderBottomWidth: 1,
    borderBottomColor: '#ddd',
    flexDirection: 'row',
    justifyContent: 'space-between',
    alignItems: 'center',
  },
});

export default TaskItem;

  1. Manejo del Estado

Integrar Componentes en App.js

Vamos a integrar los componentes TaskInput y TaskItem en App.js y manejar el estado de las tareas.

// App.js
import React, { useState } from 'react';
import { View, FlatList, StyleSheet } from 'react-native';
import TaskInput from './components/TaskInput';
import TaskItem from './components/TaskItem';

const App = () => {
  const [tasks, setTasks] = useState([]);

  const addTask = (task) => {
    setTasks((currentTasks) => [
      ...currentTasks,
      { id: Math.random().toString(), value: task },
    ]);
  };

  const deleteTask = (taskId) => {
    setTasks((currentTasks) => {
      return currentTasks.filter((task) => task.id !== taskId);
    });
  };

  return (
    <View style={styles.screen}>
      <TaskInput onAddTask={addTask} />
      <FlatList
        data={tasks}
        renderItem={(itemData) => (
          <TaskItem
            task={itemData.item.value}
            onDelete={() => deleteTask(itemData.item.id)}
          />
        )}
        keyExtractor={(item) => item.id}
      />
    </View>
  );
};

const styles = StyleSheet.create({
  screen: {
    padding: 50,
  },
});

export default App;

  1. Persistencia de Datos con AsyncStorage

Guardar y Cargar Tareas

Vamos a modificar App.js para guardar y cargar las tareas utilizando AsyncStorage.

// App.js
import React, { useState, useEffect } from 'react';
import { View, FlatList, StyleSheet } from 'react-native';
import AsyncStorage from '@react-native-async-storage/async-storage';
import TaskInput from './components/TaskInput';
import TaskItem from './components/TaskItem';

const App = () => {
  const [tasks, setTasks] = useState([]);

  useEffect(() => {
    loadTasks();
  }, []);

  useEffect(() => {
    saveTasks();
  }, [tasks]);

  const saveTasks = async () => {
    try {
      await AsyncStorage.setItem('tasks', JSON.stringify(tasks));
    } catch (error) {
      console.error('Failed to save tasks', error);
    }
  };

  const loadTasks = async () => {
    try {
      const savedTasks = await AsyncStorage.getItem('tasks');
      if (savedTasks) {
        setTasks(JSON.parse(savedTasks));
      }
    } catch (error) {
      console.error('Failed to load tasks', error);
    }
  };

  const addTask = (task) => {
    setTasks((currentTasks) => [
      ...currentTasks,
      { id: Math.random().toString(), value: task },
    ]);
  };

  const deleteTask = (taskId) => {
    setTasks((currentTasks) => {
      return currentTasks.filter((task) => task.id !== taskId);
    });
  };

  return (
    <View style={styles.screen}>
      <TaskInput onAddTask={addTask} />
      <FlatList
        data={tasks}
        renderItem={(itemData) => (
          <TaskItem
            task={itemData.item.value}
            onDelete={() => deleteTask(itemData.item.id)}
          />
        )}
        keyExtractor={(item) => item.id}
      />
    </View>
  );
};

const styles = StyleSheet.create({
  screen: {
    padding: 50,
  },
});

export default App;

  1. Estilizado de la Aplicación

Mejorar el Estilo

Vamos a mejorar el estilo de la aplicación para que sea más atractiva.

// components/TaskInput.js
import React, { useState } from 'react';
import { View, TextInput, Button, StyleSheet } from 'react-native';

const TaskInput = ({ onAddTask }) => {
  const [task, setTask] = useState('');

  const handleAddTask = () => {
    if (task.trim()) {
      onAddTask(task);
      setTask('');
    }
  };

  return (
    <View style={styles.inputContainer}>
      <TextInput
        placeholder="Add a new task"
        style={styles.input}
        value={task}
        onChangeText={setTask}
      />
      <Button title="Add" onPress={handleAddTask} />
    </View>
  );
};

const styles = StyleSheet.create({
  inputContainer: {
    flexDirection: 'row',
    justifyContent: 'space-between',
    alignItems: 'center',
    marginBottom: 20,
    borderBottomWidth: 1,
    borderBottomColor: '#ccc',
    padding: 10,
  },
  input: {
    width: '80%',
    padding: 10,
  },
});

export default TaskInput;
// components/TaskItem.js
import React from 'react';
import { View, Text, Button, StyleSheet } from 'react-native';

const TaskItem = ({ task, onDelete }) => {
  return (
    <View style={styles.taskItem}>
      <Text>{task}</Text>
      <Button title="Delete" onPress={onDelete} />
    </View>
  );
};

const styles = StyleSheet.create({
  taskItem: {
    padding: 10,
    backgroundColor: '#f9f9f9',
    borderBottomWidth: 1,
    borderBottomColor: '#ddd',
    flexDirection: 'row',
    justifyContent: 'space-between',
    alignItems: 'center',
  },
});

export default TaskItem;
// App.js
import React, { useState, useEffect } from 'react';
import { View, FlatList, StyleSheet } from 'react-native';
import AsyncStorage from '@react-native-async-storage/async-storage';
import TaskInput from './components/TaskInput';
import TaskItem from './components/TaskItem';

const App = () => {
  const [tasks, setTasks] = useState([]);

  useEffect(() => {
    loadTasks();
  }, []);

  useEffect(() => {
    saveTasks();
  }, [tasks]);

  const saveTasks = async () => {
    try {
      await AsyncStorage.setItem('tasks', JSON.stringify(tasks));
    } catch (error) {
      console.error('Failed to save tasks', error);
    }
  };

  const loadTasks = async () => {
    try {
      const savedTasks = await AsyncStorage.getItem('tasks');
      if (savedTasks) {
        setTasks(JSON.parse(savedTasks));
      }
    } catch (error) {
      console.error('Failed to load tasks', error);
    }
  };

  const addTask = (task) => {
    setTasks((currentTasks) => [
      ...currentTasks,
      { id: Math.random().toString(), value: task },
    ]);
  };

  const deleteTask = (taskId) => {
    setTasks((currentTasks) => {
      return currentTasks.filter((task) => task.id !== taskId);
    });
  };

  return (
    <View style={styles.screen}>
      <TaskInput onAddTask={addTask} />
      <FlatList
        data={tasks}
        renderItem={(itemData) => (
          <TaskItem
            task={itemData.item.value}
            onDelete={() => deleteTask(itemData.item.id)}
          />
        )}
        keyExtractor={(item) => item.id}
      />
    </View>
  );
};

const styles = StyleSheet.create({
  screen: {
    padding: 50,
    backgroundColor: '#f5f5f5',
    flex: 1,
  },
});

export default App;

  1. Pruebas y Depuración

Probar la Aplicación

  • Asegúrate de que puedes agregar, visualizar y eliminar tareas.
  • Verifica que las tareas se guardan y cargan correctamente al reiniciar la aplicación.

Depuración

  • Utiliza console.log para depurar problemas.
  • Asegúrate de manejar errores en las operaciones de AsyncStorage.

Conclusión

En este proyecto, hemos construido una aplicación de lista de tareas completa utilizando React Native. Hemos cubierto la creación de componentes, manejo del estado, persistencia de datos con AsyncStorage, y estilizado de la aplicación. Este proyecto te ha proporcionado una base sólida para construir aplicaciones más complejas en el futuro.

¡Felicidades por completar el Proyecto 1! Ahora estás listo para avanzar al siguiente proyecto y seguir mejorando tus habilidades en React Native.

© Copyright 2024. Todos los derechos reservados