Aprendiendo React: Creando una aplicación toDo (tareas)

Una vez que hemos visto las partes fundamentales de nuestra primera aplicación y conocemos el contexto de la misma, podemos comenzar a añadir elementos y extender un poco nuestro conocimiento de la librería React.

En este caso lo haremos creando una sencilla aplicación de tareas y explicando detenidamente los pasos seguidos para ello.

Preparación del entorno

Como hemos visto, el primer paso es utilizar la herramienta de creación de proyectos de react a través de la terminal con el comando (tutorial):

npx create-react-app

Una vez creado esto dispondremos de una estructura de archivos que simplificaremos más.

Pasaremos de esto:

a esto:

Hemos borrado de la carpeta src todo menos el index.js y hemos creado una carpeta en la que colocaremos nuestros componentes. Es importante comenzar a desarrollar un modelo organizado de trabajo para facilitar el desarrollo de los proyectos una vez estos se hagan grandes.

Crearemos dos componentes con extensión jsx dentro de la carpeta «components», estos serán nuestros primeros elementos modulares: TodoItem.jsx y TodoList.jsx.

*Un consejo a la hora de programar es el de tratar de hacerlo en un entorno que nos resulte cercano y ofrezca facilidades, en mi caso utilizo visual estudio code, y entre sus extensiones encontramos alguna como:

Que nos permite generar a través de snippets (o atajos) la estructura base de los componentes con la combinación rfc+intro:

Codificando la aplicación:

Trabajaremos con 5 documentos únicamente:

  • index.html: Es el documento que usaremos como layout de la aplicación, concretamente su div root.
  • index.js: Es el script que se encarga de renderizar los datos de la aplicación en el layout del index.
  • App.jsx: Lógica del programa, es el script que se encarga de la funcionalidad y definir la interfaz, lo veremos en más detalle.
  • TodoList.jsx: Es uno de los dos componentes que usaremos en esta aplicación, su estructura es sencilla, exporta una función Todolist.
  • TodoItem.jsx: El otro de los componentes básicos de la aplicación, define la estrcutura de los items.

Código y explicación:

Comencemos por los códigos más sencillos para poder explicar al final, de manera más detallada, la lógica de app.jsx, la modularidad natural de react facilita mucho la explicación de una aplicación a través de sus fragmentos.

Index.html:

<!DOCTYPE html>
<html lang="es">
  <head>
    <meta charset="utf-8" />
    <link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
    <meta name="viewport" content="width=device-width, initial-scale=1" />
    <meta name="theme-color" content="#000000" />
    <meta
      name="description"
      content="Web site created using create-react-app"
    />
    <link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png" />
    
    <link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
   
    <title>React App</title>
  </head>
  <body>
    <noscript>You need to enable JavaScript to run this app.</noscript>
    <div id="root"></div>
    
  </body>
</html>

Es bastante autoexplicativo, lo único que debemos tener clara es la ubicación del div: root, donde se mostrará la información.

index.js:

import React from "react";
import ReactDOM from "react-dom";
import { App } from "./App";

ReactDOM.render(<App />, document.getElementById("root"));

Este breve código (Obviando los imports, consisten en un renderizado de la app en el elemento root seleccionado a través de su Id.

TodoList.jsx y TodoItem.jsx

import React from "react";
import { TodoItem } from "./TodoItem";

export function TodoList({ todos, toggleTodo }) {
  return (
    <ul>
      {todos.map((todo) => (
        <TodoItem key={todo.id} todo={todo} toggleTodo={toggleTodo} />
      ))}
    </ul>
  );
}

Podemos ver la creación de un componente a través de la creación de un mapa de elementos y se recurre a los toggle para controlar el estado de las tareas.

import React from "react";

export function TodoItem({ todo, toggleTodo }) {
  const { id, task, completed } = todo;

  const handleTodoClick = () => {
    toggleTodo(id);
  };

  return (
    <li>
      <input type="checkbox" checked={completed} onChange={handleTodoClick} />
      {task}
    </li>
  );
}

El script TodoItem define la estructura de los elementos que compondrán la lista previa, podemos ver la referencia a un evendo de cambio de estado.

App.jsx:

import React, { Fragment, useState, useRef, useEffect } from "react";
import { v4 as uuidv4 } from "uuid";
import { TodoList } from "./components/TodoList";

const KEY = "todoApp.todos";

export function App() {
  const todoTaskRef = useRef();
  const [todos, setTodos] = useState([
    { id: 1, task: "Aprender React", completed: false },
    { id: 2, task: "Hacer ejercicios de React", completed: false },
  ]);

  useEffect(() => {
    const storedTodos = JSON.parse(localStorage.getItem(KEY));
    if (storedTodos) {
      setTodos(storedTodos);
    }
  }, []);

  useEffect(() => {
    localStorage.setItem(KEY, JSON.stringify(todos));
  }, [todos]);

  const toggleTodo = (id) => {
    const newTodos = [...todos];
    const todo = newTodos.find((todo) => todo.id === id);
    todo.completed = !todo.completed;
    setTodos(newTodos);
  };

  const handleTodoAdd = (event) => {
    const task = todoTaskRef.current.value;
    if (task === "") return;

    setTodos((prevTodos) => {
      return [...prevTodos, { id: uuidv4(), task: task, completed: false }];
    });

    todoTaskRef.current.value = ""; // Limpiamos el campo de entrada
  };

  const handleClearAll = () => {
    const newTodos = todos.filter((todo) => !todo.completed);
    setTodos(newTodos);
  };

  return (
    <Fragment>
      <h2>Lista de tareas pendientes:</h2>
      <TodoList todos={todos} toggleTodo={toggleTodo} />
      <input ref={todoTaskRef} type="text" placeholder="Nueva tarea" />
      <button onClick={handleTodoAdd}>Añadir</button>
      <button onClick={handleClearAll}>Eliminar</button>
      <div>
        {todos.filter((todo) => !todo.completed).length > 0 ? (
          <div>
            <p>Te quedan tareas por terminar:</p>
            <ul>
              {todos.map((todo) =>
                !todo.completed ? (
                  <li key={todo.id}>{todo.task}</li>
                ) : null
              )}
            </ul>
          </div>
        ) : null}
      </div>
    </Fragment>
  );
}

Podemos ver que la lógica dentro del script App.jsx es la más amplia con diferencia, a pesar de que la explicaremos por su estructura actual, animamos a quien esté aprendiendo a sustituir elementos, modificarlos e incluso a añadir unos nuevos, el aprendizaje pasa por la prueba y el error!.

  • Importaciones de React y otros módulos:
    • Importamos React, que es la biblioteca principal que usamos para construir la interfaz de usuario.
    • Importamos varias funciones y componentes adicionales de React que usaremos en nuestro código.
    • También importamos un módulo llamado uuidv4, que se usa para generar identificadores únicos para nuestras tareas.
    • Importamos el componente TodoList desde un archivo llamado TodoList.js.
  • Definición de una constante KEY:
    • Creamos una constante llamada KEY que se utilizará como clave para almacenar y recuperar los datos de nuestras tareas en el almacenamiento local del navegador.
  • Definición del componente App:
    • Creamos una función llamada App que representa nuestra aplicación principal.
    • Usamos useState para definir una variable de estado llamada todos que almacenará la lista de tareas. Inicialmente, esta lista contiene dos tareas con sus nombres y estados iniciales.
    • Usamos useRef para crear una referencia llamada todoTaskRef. Esta referencia se utiliza para acceder al valor del campo de entrada de texto donde el usuario ingresará nuevas tareas.
    • Utilizamos useEffect para realizar efectos secundarios en nuestra aplicación. En el primer useEffect, cargamos las tareas almacenadas previamente en el almacenamiento local y las establecemos como el estado inicial de todos. En el segundo useEffect, actualizamos el almacenamiento local cada vez que todos cambia.
  • Definición de funciones auxiliares:
    • toggleTodo(id): Esta función se utiliza para cambiar el estado (completado o pendiente) de una tarea específica cuando se hace clic en ella.
    • handleTodoAdd(event): Esta función se llama cuando el usuario presiona el botón «Añadir». Agrega una nueva tarea a la lista de tareas utilizando el valor ingresado por el usuario y restablece el campo de entrada de texto.
    • handleClearAll(): Esta función se llama cuando el usuario presiona el botón «Eliminar». Filtra y actualiza la lista de tareas para eliminar todas las tareas completadas.
  • Renderización del componente:
    • En el bloque de retorno, definimos la estructura de nuestra interfaz de usuario.
    • Mostramos un título, una lista de tareas (utilizando el componente TodoList), un campo de entrada de texto para agregar nuevas tareas y botones para agregar y eliminar tareas.
    • Además, mostramos una sección que muestra las tareas pendientes solo si hay tareas pendientes en la lista. En este bloque, enumeramos las tareas pendientes, es una función redundante pero nos permite practicar más con las llamadas a los elementos.

Resumen:

Esta pequeña aplicación nos ha permitido ver el funcionamiento claro de cómo se envían los datos desde la lógica a la vista (index.html), ver la relevancia del app.jsx y entender lo útil que es la modularidad de los componentes de React, también hemos tenido un acercamiento a los estados, que se explicarán en detalle en otro post, al igual que los hooks y los props, elementos troncales de la programación con React.

Fuente de este ejercicio: vídeo.

¿Te ha resultado útil?

Promedio de puntuación 0 / 5. Recuento de votos: 0