DEV Community

Isaac Cantún
Isaac Cantún

Posted on

TODO app con React (ejemplo básico)

Tabla de contenido

  1. Introducción
  2. Instalando create-react-app y creando nuestra aplicación
  3. Proyecto a realizar
  4. Primeros pasos
  5. Construyendo la lógica de nuestra aplicación
  6. Puntos finales

Introducción

¡Hola que tal! en los anteriores artículos estuvimos aprendiendo React, pero lo estábamos haciendo de una manera muy básica y sin tantas complicaciones porque el propósito era que aprendiéramos las bases de esta librería, sin embargo no es la forma correcta de trabajar porque cuando trabajamos en aplicaciones reales, necesitamos instalar paquetes, mejorar la arquitectura de nuestras carpetas y algunas cosas más que con simplemente dos scripts es muy difícil, pero no hay de que preocuparse, en esta ocasión vamos a aprender a trabajar con create-react-app, pero ¿qué es create-react-app? según la documentación oficial:

create-react-app is:

Create React App is an officially supported way to create single-page React applications. It offers a modern build setup with no configuration.

¡¡En español por favor!!, bien, es un proyecto desarrollado por el equipo que creó React, nos permite comenzar a trabajar sin "configuraciones", si, entre comillas porque al final de cuentas cuando un proyecto crece siempre hay cosas que configurar, sin embargo esta es una buena manera de empezar, pero antes de que comencemos tienes que tener en cuenta algunas cosas:

  • Necesitas conocer la terminal de tu sistema operativo 💻.
  • Tener instalado Node Js.
  • Tener bases de JavaScript, HTML y CSS.

Instalando create-react-app y creando nuestra aplicación

Para empezar y sin tanto rollo vamos a nuestra terminal a ubicarnos en una carpeta que ya tengas preparada para tus proyectos y ejecutamos el siguiente comando npx create-react-app my-todo, bueno quizá si ya conocías algo de Node Js te preguntarás ¿ y npm?, actualmente la documentación de create-react-app recomienda utilizar npx y esto es porque anteriormente requeríamos instalar create-react-app como una dependencia global para poder utilizar la CLI que nos proporciona, npx se encarga de la instalación (no global) y la creación de nuestro proyecto, dos acciones en un solo paso, así de simple (quizá haya mas que agregar pero esto es lo que nos importa por ahora), continuando con nuestro proyecto, si ya ha terminado de descargar e instalar todas las dependencias deberíamos poder ver dentro de nuestra carpeta de proyectos una nueva llamada my-todo.

Si la abrimos en nuestro editor de código favorito (en mi caso utilizo visual studio code) podemos ver la siguiente estructura de carpetas:

estructura de carpetas

Muy bien en lo que respecta a este artículo no voy a explicar cada archivo, porque no es el objetivo, así que vamos a continuar. Todo el trabajo que vamos a realizar en este proyecto lo haremos dentro de la carpeta src, así que antes de iniciar tenemos que ejecutar nuestro servidor de desarrollo, para levantarlo ejecutamos el comando npm start, puedes usar Yarn si lo tienes instalado, Yarn es un gestor de paquetes para JavaScript creado por Facebook, de hecho yo utilizo Yarn, sin embargo por esta ocasión y por motivos de rapidez usaremos npm, podemos ver la siguiente salida en nuestra terminal lo cual significa que nuestro servidor se esta ejecutando:

terminal

Ahora si nos dirigimos a nuestro navegador y visitamos http://localhost:3000 encontramos corriendo nuestra aplicación de React:

pantalla

Proyecto a realizar

Nuestro proyecto será una clásica, simple y básica aplicación de tareas, quizá puede que sea muy sencilla, pero aprenderemos lo que necesitamos saber de react-create-app, en posteriores artículos iremos trabajando en ejemplos mas complejos, incluso al final de este post, tendrás algunos "retos" o mejoras que podrás hacerle a la aplicación, y poner en práctica lo aprendido, ¡manos a la obra!.

Primeros pasos

Vamos de lleno al código, vamos a modificar algunos archivos para poder comenzar a trabajar, abrimos el archivo src/App.css y eliminamos el código y copiamos lo siguiente:

.App {
  text-align: center;
}

.App-logo {
  height: 40vmin;
  pointer-events: none;
}

@media (prefers-reduced-motion: no-preference) {
  .App-logo {
    animation: App-logo-spin infinite 20s linear;
  }
}

.App-content {
  background-color: #61dafb;
  min-height: 100vh;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  font-size: calc(10px + 2vmin);
  color: white;
}

.App-link {
  color: #61dafb;
}

@keyframes App-logo-spin {
  from {
    transform: rotate(0deg);
  }
  to {
    transform: rotate(360deg);
  }
}

form input {
  height: 26px;
  border-radius: 5%;
  display: flex;
  vertical-align: auto;
}

form button {
  cursor: pointer;
  display: inline-block;
  text-align: center;
  text-decoration: none;
  margin: 2px 0;
  border: solid 1px transparent;
  border-radius: 4px;
  padding: 0.5em 1em;
  color: #ffffff;
  background-color: darkgreen;
  height: 30px;
  width: 120px;
}

h3 {
  margin: 0;
}

.list {
  display: flex;
  margin: 5px;
  flex-direction: row;
  align-items: flex-end;
}

.btn-delete {
  cursor: pointer;
  display: inline-block;
  text-align: center;
  text-decoration: none;
  border: solid 1px transparent;
  border-radius: 4px;
  color: #ffffff;
  background-color: red;
  height: 30px;
  width: 30px;
}
Enter fullscreen mode Exit fullscreen mode


`

Y ahora en src/App.js eliminamos el contenido y copiamos lo siguiente:

`

import React from 'react';
import './App.css';

const  App = () => {

  return (
    <div className="App">
      <div className="App-content">
        <p>
          Aquí haremos nuestro TO-DO list
        </p>
      </div>
    </div>
  );
}

export default App;

Enter fullscreen mode Exit fullscreen mode

te explico rápidamente ya que esto no es muy relevante, solo hice unas pequeñas modificaciones a la hoja de estilos que usaremos y al componente App el cual solo lo convertimos a una arrow function, así que continuemos si vamos a nuestro navegador podemos ver los cambios realizados:

modificaciones

Es todo, no necesitamos nada más con lo que a estilos corresponde, vamos a empezar con la parte ruda, lo primero que tenemos que hacer es crear una carpeta en la raíz de src a la cual llamaremos components, obviamente y como lo imaginas ahí estarán nuestros componentes, para este ejemplo solo crearemos 2, vamos al primero, dentro de components creamos un archivo llamado Todo.jsx, antes de ir al código te explico, con React podemos utilizar la extensión .js o .jsx para nuestros componentes sin ningún problema, el motivo por el cual yo uso la extensión .jsx es únicamente por mi editor de código, ya que con esta extensión tengo un mejor autocompletado, continuemos agregamos el siguiente código dentro de nuestro componente Todo:

import React from 'react'


const Todo = () => {
    return (
        <h1>Todo component</h1>
    )
}

export default Todo
Enter fullscreen mode Exit fullscreen mode

Y ahora también dentro de la carpeta components creamos un archivo llamado Form.jsx con el siguiente código:

import React from 'react'


const Form = () => {
    return (
        <h1>Form component</h1>
    )
}

export default Form
Enter fullscreen mode Exit fullscreen mode

Ahora que tenemos ambos componentes hagamos lo siguiente dentro del componente Form, agregamos lo siguiente:

import React from 'react'
import Todo from './Todo'


const Form = () => {
    return (
        <>
            <h1>Form component</h1>
            <Todo />
        </>
    )
}

export default Form
Enter fullscreen mode Exit fullscreen mode

Y ahora en nuestro componente App hacemos lo siguiente:

import React from 'react';
import './App.css';
import Form from './components/Form';

const  App = () => {

  return (
    <div className="App">
      <div className="App-content">
        <p>
          Aquí haremos nuestro TO-DO list
        </p>
        <Form />
      </div>
    </div>
  );
}

export default App;
Enter fullscreen mode Exit fullscreen mode

En nuestro navegador ya podemos ver que nuestros dos componentes se han incluido correctamente:

form y todo

Construyendo la lógica de nuestra aplicación

Hasta este punto tenemos todo lo que necesitamos para trabajar, la mayor parte del trabajo lo vamos a realizar en nuestro componente Form así que vamos a el, estamos construyendo una aplicación con tareas, entonces necesitamos las tareas (si ya se repetí la palabra tareas, pero es para que no lo olvides), entonces lo primero que haremos es usar uno de nuestros queridos Hooks para esto, vamos al código:

import React, {useState} from 'react'
import Todo from './Todo'


const Form = () => {
    const [todos, setTodos] = useState([
        {todo: 'todo 1'},
        {todo: 'todo 2'},
        {todo: 'todo 3'}
    ])

    return (
        <>
            <h1>Form component</h1>
            <Todo />
        </>
    )
}

export default Form
Enter fullscreen mode Exit fullscreen mode

Lo que hicimos aquí fue importar useState y declarar un estado dentro de nuestro componente Form, el cual es un array de objetos que serán nuestras tareas, bien pero esto aún no hace absolutamente nada, así que arreglemos eso, en nuestro componente Todo realizamos los siguientes cambios:

import React from 'react'


const Todo = ({todo}) => {
    return (
        <>
            <h3>{todo}</h3>
        </>
    )
}

export default Todo
Enter fullscreen mode Exit fullscreen mode

Eliminamos la etiqueta h1 y en su lugar agregamos un h3 el cual se encarga de imprimir un prop que es únicamente el nombre de nuestra tarea, vamos de nuevo al componente Form y agregamos lo siguiente:

import React, {useState} from 'react'
import Todo from './Todo'


const Form = () => {
    const [todos, setTodos] = useState([
        {todo: 'todo 1'},
        {todo: 'todo 2'},
        {todo: 'todo 3'}
    ])

    return (
        <>
            {
                todos.map((value, index) => (
                    <Todo todo={value.todo} />
                ))
            }
        </>
    )
}

export default Form
Enter fullscreen mode Exit fullscreen mode

Muy bien, lo que hacemos es recorrer nuestro array de tareas (todos) el cual habíamos inicializado con tres objetos, dentro de la función map incluímos nuestro componente Todo y le pasamos el prop que necesita, si vamos al navegador tenemos el siguiente resultado:

tareas renderizadas

¡Perfecto! ya tenemos nuestras tareas renderizadas, pero ahora necesitamos empezar a agregar más tareas, vamos al código, vamos a realizar unas modificaciones algo amplias en nuestro componente Form para que se vea así:

import React, {useState} from 'react'
import Todo from './Todo'


const Form = () => {
    const [todo, setTodo] = useState({})
    const [todos, setTodos] = useState([
        {todo: 'todo 1'},
        {todo: 'todo 2'},
        {todo: 'todo 3'}
    ])

    const handleChange = e => setTodo({[e.target.name]: e.target.value})

    const handleClick = e => console.log('click click')

    return (
        <>
            <form onSubmit={e => e.preventDefault()}>
                <label>Agregar tarea</label><br />
                <input type="text" name="todo" onChange={handleChange}/>
                <button onClick={handleClick}>agregar</button>
            </form>
            {
                todos.map((value, index) => (
                    <Todo todo={value.todo} />
                ))
            }
        </>
    )
}

export default Form
Enter fullscreen mode Exit fullscreen mode

Voy a tratar de explicarte a detalle cada cambio, vamos a empezar con la nueva constante que definimos arriba de nuestro estado de TAREAS, agregamos este fragmento de código const [todo, setTodo] = useState({}), el cual inicializa un nuevo estado que nos va a servir para agregar UNA tarea, ya que el estado anterior, nos ayuda a crear UNA LISTA de TAREAS, una vez aclarado esto, vamos a la siguiente, la función const handleChange = e => setTodo({[e.target.name]: e.target.value}), captura el evento change de nuestro input el cual se mira ahora así <input type="text" name="todo" onChange={handleChange}/>, si te fijas el input tiene un atributo name en el cual el nombre es el mismo que la key de nuestros objetos (tareas), esto es porque en la función handleChange recibimos como parámetro el evento como tal y esta representado con la variable e, e nos permite acceder a algunas propiedades entre ellas al name del input y al value del mismo y estos se encuentran dentro de target, es por eso que hacemos setTodo({[e.target.name]: e.target.value}) dentro de handleChange, modificamos el estado de nuestra aplicación para capturar una nueva tarea, la siguiente función se llama handleClick, su única función en este momento es imprimir en consola un mensaje, pero mas adelante eso cambiará, para terminar con los detalles nuestro formulario también ejecuta un evento, solo que esta vez no creamos una nueva función sino que ejecutamos una arrow function directamente, de nuevo capturamos el evento y hacemos un e.preventDefault(), si vienes de jQuery creo que sabes perfectamente para que sirve esto, y si no sabes pues es para que al hacer submit de nuestro formulario no se refresque nuestro navegador, bien por último nuestro botón ejecuta en el evento onClick la función handleClick, si en efecto esa que solo imprime un mensajito, perfecto, teniendo ya todo esto vamos a nuestro componente Todo para realizar unas modificaciones y se vea de la siguiente manera:

import React from 'react'

const Todo = ({todo, index, deleteTodo}) => {
    return (
        <>
            <div className="list">
                <h3>{todo}</h3> <button className="btn-delete" onClick={() => deleteTodo(index)}>x</button>
            </div>
        </>
    )
}

export default Todo
Enter fullscreen mode Exit fullscreen mode

Aquí no hay mucho que explicar, agregamos un boton con una clase btn-delete, OJO en react no podemos usar la palabra class en nuestro código jsx, recuerda que jsx NO es HTML sino una extensión del lenguaje JavaScript y class es una palabra reservada del lenguaje, en su lugar usamos className, aclarado este punto, continuemos, agregamos dos props más index y deleteTodo, estas dos propiedades la cual una es un entero y la otra es una función nos ayudarán mas adelante para eliminar tareas, la función deleteTodo se ejecuta en el evento onClick del botón que acabamos de agregar (comunicación hijos a padres, ¿recuerdas?), hemos terminado nuestro componente Todo aquí ya no haremos más.

Volvamos a nuestro componente Form modificamos handleclick y de igual manera agregamos un nuevo método, así es deleteTodo que será el prop que le pasaremos a nuestro componente Todo al igual que index, entonces el código se vería de la siguiente manera.

const handleClick = e => {
    if(Object.keys(todo).length === 0 || todo.todo.trim() === '') {
        alert('el campo no puede estar vacio')
        return
    }
    setTodos([...todos, todo])
}

const deleteTodo = indice => {
    const newTodos = [...todos]
    newTodos.splice(indice, 1)
    setTodos(newTodos)
}
Enter fullscreen mode Exit fullscreen mode

La función handleClick ahora ya tiene una funcionalidad, lo primero que hacemos es validar que nuestro input no este vacío y después solo lo agregamos al estado, hacemos uso del spread operator dentro de setTodos para agregar nuestra nueva tarea y conservar todas las que tenemos, y la función deleteTodo obtiene todas las tareas, y elimina la tarea con el indice que le pasamos como parámetro y por último actualizamos el estado con la nueva lista de tareas, para terminar con nuestro ejemplo vamos a modificar la función map dentro del return para pasarle a Todo los props que necesita:

todos.map((value, index) => (
   <Todo todo={value.todo} key={index} index={index} deleteTodo={deleteTodo}/>
))
Enter fullscreen mode Exit fullscreen mode

Y nuestra aplicación se ve así:
fin

Puntos finales

Hemos terminado nuestra aplicación, la cual funciona sin embargo, hay muchas cosas que podemos mejorar, puedes probar tratando de mejorarlas, por ejemplo nuestra aplicación debería poder modificar una tarea específicamente, quizá marcarla como completada, quizá la lista de tareas podamos tenerla en un componente por separado y definitivamente mejorar el diseño, eres libre de clonar el repositorio de Github y mirar el código y mejorarlo, espero que te haya sido útil y nos vemos en el próximo artículo, saludos y ¡Happy Coding!

Top comments (1)

Collapse
 
alexsatan5 profile image
Alexcode

Muchas gracias excelente articulo, saludos desde Nicaragua!