TODO app con React (ejemplo básico)
Tags: react, frontend, javascript, functional
Published: 2020-04-29 18:23:12

---
series: react-fundamentals
---

# Tabla de contenido
1. [Introducción](#introduccion)
2. [Instalando create-react-app y creando nuestra aplicación](#instalacion)
3. [Proyecto a realizar](#proyecto)
4. [Primeros pasos](#primeros-pasos)
5. [Construyendo la lógica de nuestra aplicación](#logica)
6. [Puntos finales](#final)

##Introducción <a name="introduccion">

¡Hola que tal! en los anteriores artículos estuvimos aprendiendo [React](https://reactjs.org), 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](https://create-react-app.dev/), 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](https://nodejs.org/es/). 
- Tener bases de *JavaScript, HTML y CSS.*

##Instalando create-react-app y creando nuestra aplicación <a name="instalacion">

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](https://code.visualstudio.com/)*) podemos ver la siguiente estructura de carpetas:

![estructura de carpetas](https://i.ibb.co/RgHvcsV/estructura.png)

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](https://yarnpkg.com/) 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](https://i.ibb.co/yQvypCY/consola.png)

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

![pantalla](https://i.ibb.co/dg17h4n/pantalla.png)

## Proyecto a realizar <a name="proyecto">

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 <a name="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:

```css
.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;
}
````

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

````react
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;

```

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](https://i.ibb.co/X3SL4xF/cambios.png) 

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`:

```react
import React from 'react'


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

export default Todo
```

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

```react
import React from 'react'


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

export default Form
```

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

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


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

export default Form
```

Y ahora en nuestro componente `App` hacemos lo siguiente:

```react
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;
```

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

![form y todo](https://i.ibb.co/WDd8Zn7/form-y-todo.png)

## Construyendo la lógica de nuestra aplicación <a name="logica">

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:

```react
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
```

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:

```react
import React from 'react'


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

export default Todo
```

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:

```react
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
```

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](https://i.ibb.co/XkLC4hn/tareas-navegador.png)

¡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í:

```react
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
```

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:

```react
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
```

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.

```react
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)
}
```

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:

```react
todos.map((value, index) => (
   <Todo todo={value.todo} key={index} index={index} deleteTodo={deleteTodo}/>
))
```

Y nuestra aplicación se ve así:
 ![fin](https://i.ibb.co/rxydQ9g/fin.png)

## Puntos finales <a name="final">

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](https://github.com/izakntun/todo-app) 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!