No tienes acceso a esta clase

¡Continúa aprendiendo! Únete y comienza a potenciar tu carrera

Aprende todo un fin de semana sin pagar una suscripción 🔥

Aprende todo un fin de semana sin pagar una suscripción 🔥

Regístrate

Comienza en:

5D
13H
27M
24S
Curso de Introducción a React.js

Curso de Introducción a React.js

Juan David Castro Gallego

Juan David Castro Gallego

Formulario para crear TODOs

19/23
Recursos

Algo muy importante al crear formularios es tener en cuenta que React funciona un poco diferente al HTML, ya que en HTML conservan naturalmente algún estado interno.

En React nosotros podemos mutar el estado de nuestros componentes con el hook de estado, un componente controlado es simplemente un componente en el que sus valores son controlados por React.

Creando el formulario para crear un nuevo TODO

En nuestro archivo AppUi.js importaremos y añadiremos un componente TodoForm:

import React from "react";
import { TodoContext } from "../TodoContext";
import { TodoCounter } from "../TodoCounter";
import { TodoSearch } from "../TodoSearch";
import { TodoList } from "../TodoList";
import { TodoItem } from "../TodoItem";
import { TodoForm } from "../TodoForm";
import { CreateTodoButton } from "../CreateTodoButton";
import { Modal } from "../Modal";

function AppUI() {
	const { error, loading, searchedTodos, completeTodo, deleteTodo, openModal, setOpenModal } =
		React.useContext(TodoContext);
        
	return (
		
			
			
			
				{error && 

Desespérate, hubo un error...

} {loading &&

Estamos cargando, no desesperes...

} {!loading && !searchedTodos.length &&

¡Crea tu primer TODO!

} {searchedTodos.map((todo) => ( completeTodo(todo.text)} onDelete={() => deleteTodo(todo.text)} /> ))}
{!!openModal && ( )}
); } export { AppUI };

En un momento crearemos este componente, primero necesitamos añadir una función para añadir nuestro nuevo TODO, dentro de nuestro contexto para utilizarla en nuestro formulario.

TodoContext

import React from 'react';
import { useLocalStorage } from './useLocalStorage';

const TodoContext = React.createContext();

function TodoProvider(props) {
  const {
    item: todos,
    saveItem: saveTodos,
    loading,
    error,
  } = useLocalStorage('TODOS_V1', []);
  const [searchValue, setSearchValue] = React.useState('');
  const [openModal, setOpenModal] = React.useState(false);

  const completedTodos = todos.filter(todo => !!todo.completed).length;
  const totalTodos = todos.length;

  let searchedTodos = [];

  if (!searchValue.length >= 1) {
    searchedTodos = todos;
  } else {
    searchedTodos = todos.filter(todo => {
      const todoText = todo.text.toLowerCase();
      const searchText = searchValue.toLowerCase();
      return todoText.includes(searchText);
    });
  }
  // Función para añadir un nuevo TODO
  const addTodo = (text) => {
    const newTodos = [...todos];
    newTodos.push({
      completed: false,
      text,
    });
    saveTodos(newTodos);
  };

  const completeTodo = (text) => {
    const todoIndex = todos.findIndex(todo => todo.text === text);
    const newTodos = [...todos];
    newTodos[todoIndex].completed = true;
    saveTodos(newTodos);
  };

  const deleteTodo = (text) => {
    const todoIndex = todos.findIndex(todo => todo.text === text);
    const newTodos = [...todos];
    newTodos.splice(todoIndex, 1);
    saveTodos(newTodos);
  };
  
  return (
    <TodoContext.Provider value={{
      loading,
      error,
      totalTodos,
      completedTodos,
      searchValue,
      setSearchValue,
      searchedTodos,
      addTodo,
      completeTodo,
      deleteTodo,
      openModal,
      setOpenModal,
    }}>
      {props.children}
    TodoContext.Provider>
  );
}

export { TodoContext, TodoProvider };

TodoForm.js

Ahora que ya tenemos prácticamente todo, solo queda utilizar la función addTodo para añadir nuestro TODO desde nuestro modal.

import React from 'react';
import { TodoContext } from '../TodoContext';
import './TodoForm.css';

function TodoForm() {
  // Creamos un estado para nuestro nuevo TODO
  const [newTodoValue, setNewTodoValue] = React.useState('');
  // Desestructuramos las funciones que necesitamos para añadir un TODO y cerrar nuestro modal
  const {
    addTodo,
    setOpenModal,
  } = React.useContext(TodoContext);
  
  // Creamos una función para actualizar el estado de nuestro nuevo TODO
  const onChange = (event) => {
    setNewTodoValue(event.target.value);
  };
  
  // Función para cerrar el modal
  const onCancel = () => {
    setOpenModal(false);
  };
  
  // Función para agregar nuestro nuevo TODO
  const onSubmit = (event) => {
    // prevent default para evitar recargar la página
    event.preventDefault();
    // Utilizamos nuestra función para añadir nuestro TODO
    addTodo(newTodoValue);
    // Cerramos nustro modal
    setOpenModal(false);
    // También estaría bien resetear nuestro formulario
    setNewTodoValue('')
  };

  return (
    <form onSubmit={onSubmit}>
      <label>Escribe tu nuevo TODOlabel>
      <textarea
        value={newTodoValue}
        onChange={onChange}
        placeholder="Cortar la cebolla para el almuerzo"
      />
      <div className="TodoForm-buttonContainer">
        <button
          type="button"
          className="TodoForm-button TodoForm-button--cancel"
          onClick={onCancel}
          >
          Cancelar
        button>
        <button
          type="submit"
          className="TodoForm-button TodoForm-button--add"
        >
          Añadir
        button>
      div>
    form>
  );
}

export { TodoForm };

También le podemos añadir unos estilos a nuestro formulario:

TodoForm.css

form {
  width: 90%;
  max-width: 300px;
  background-color: #fff;
  padding: 33px 40px;
  display: flex;
  justify-content: center;
  align-items: center;
  flex-direction: column;
}

label {
  text-align: center;
  font-weight: bold;
  font-size: 20px;
  color: #1E1E1F;
  margin-bottom: 26px;
}

textarea {
  background-color: #F9FBFC;
  border: 2px solid #202329;
  border-radius: 2px;
  box-shadow: 0px 5px 50px rgba(32, 35, 41, 0.25);
  color: #1E1E1F;
  font-size: 20px;
  text-align: center;
  padding: 12px;
  height: 96px;
  width: calc(100% - 25px);
}

textarea::placeholder {
  color: #A5A5A5;
  font-family: 'Montserrat';
  font-weight: 400;
}

textarea:focus {
  outline-color: #61DAFA;
}

.TodoForm-buttonContainer {
  margin-top: 14px;
  display: flex;
  justify-content: space-between;
  align-items: center;
  width: 100%;
}

.TodoForm-button {
  cursor: pointer;
  display: inline-block;
  font-size: 20px;
  color: #202329;
  font-weight: 400;
  width: 120px;
  height: 48px;
  border-radius: 2px;
  border: none;
  font-family: 'Montserrat';
}

.TodoForm-button--add {
  background: #61DAFA;
  box-shadow: 0px 5px 25px rgba(97, 218, 250, 0.5);
}

.TodoForm-button--cancel {
  background: transparent;
}

Si tienes un estilo parecido al resultado final, te animamos a que le des tu personalidad a tu aplicación, no solamente de estilos, también agrega nuevas funcionalidades, rompe el código, mejóralo, la práctica es algo de lo más importante para aprender.
¡Esperamos ver tus resultados!

Contribución creada por: Brandon Argel.

Aportes 101

Preguntas 17

Ordenar por:

¿Quieres ver más aportes, preguntas y respuestas de la comunidad?

o inicia sesión.

Aqui el CSS del archivo TodoForm.css de la carpeta TodoForm

form {
  width: 90%;
  max-width: 300px;
  background-color: #fff;
  padding: 33px 40px;
  display: flex;
  align-content: center;
  flex-direction: column;
}

label {
  text-align: center;
  font-weight: bold;
  font-size: #1E1E1F;
  margin-bottom: 26px;
}

textarea {
  background-color: #F9FBFC;
  border: 2px, solid #202329;
  border-radius: 2px;
  box-shadow: 0px 5px 50px rgba(32, 35, 41, 0.25);
  color: #1E1E1F;
  font-size: 20px;
  text-align: center;
  padding: 12px;
  height: 96px;
  width: calc(100%-25px);
}

textarea::placeholder {
color: #a5a5a5;
font-family: 'Montserrat';
font-weight: 400;
}

textarea:focus {
  outline-color: #61DAFA;
}

.TodoForm-buttonContainer {
  margin-top: 14px;
  display: flex;
  justify-content: space-between;
  align-items: center;
  width: 100%;
}

.TodoForm-button {
  cursor: pointer;
  display: inline-block;
  font-size: 20px;
  color: #202329;
  font-weight: 400;
  width: 120px;
  height: 48px;
  border-radius: 2px;
  border: none;
  font-family: 'Montserrat';
}

.TodoForm-button-add {
  background-color: #61DAFA;
  box-shadow: 0px 5px 25px rgba(97, 218, 250, 0.5);
}

.TodoForm-button-cancel {
  background: transparent;
}

Codigo del archivo index.js de la carpeta TodoForm

import './ToDoForm.css'


<form onSubmit={onSubmit} >
      <label>Escribe tu nuevo To Do</label>
      <textarea
        value = {newTodoValue}
        onChange = {onChange}
        placeholder = "Escribe una nueva tarea"
      />
      <div className="TodoForm-buttonContainer">
        <button
          type="button"
          className="TodoForm-button TodoForm-button-cancel"
          onClick = {onCancel}
        >
          Cancelar
        </button>

        <button
          className="TodoForm-button TodoForm-button-add"
          type= "submit"
        >
          Añadir
          </button>
      </div>
    </form>

NOTA: Recuerden importar el archivvo CSS en el archivo index.js 🧐

Yo quise variar un poco de lo que estaba haciendo el profesor y para la creación de tareas no hice modal pero hice uno para la eliminación de las misma.

También hice un botón al lado del buscador para ocultar las tareas completadas sin necesidad de eliminarlas

Un consejo cuando hagáis un textarea en css podéis bloquear el resize para que no aparezca en la esquina inferior derecha ya que si lo mueven se rompe todo el css.

Para ellos solo tenéis que añadir esta propiedad en css:

resize: none;

Realicé pequeños cambios en el CSS de los archivos creados en clases anteriores.

Así quedó 😃

Acabo de tener un Flashback. Ese truco de prevState => !prevState lo habia aprendido en otro lado. Me siento feliz de recordarlo ❤️

el curso es muy bueno y no se tanto de javascript asi que me perdia en algunas cosas, pero luego hice una pausa y me sente a analizar todo el codigo y hacer lo del modal sin usar react context y todo y si pude XD
me emociona la explicación del profe, me quedo enganchada y si yo se que todo es practica pero les juro que cuando intente hacer react con otros videos, solo logre usar el estado y el useEffect y ni siquiera lo entendia al nivel que lo entiendo ahora, era mas como ensayo y error, esto me funciona, esto no, inclusive hicimos (porque es un proyecto grupal el que estoy haciendo ahora) un bucle infinito por el useEffect y nos bloquearon por un dia de firebase jajajaja

Creo que la funcion onSubmit debe tener una condicion para evitar que agreguemos Todos vacios.
Asi lo hice yo:

const onSubmit =(e) =>{
        e.preventDefault();
        if(newTodoValue.length <= 0)return;
        addTodo(newTodoValue);
        setOpenModal(false);

    }

Cito un poco la documentación de React - Compononentes controlados para dar un poco de contexto:
.

Los elementos de formularios en HTML funcionan un poco diferente a otros elementos del DOM en React, debido a que los elementos de formularios conservan naturalmente algún estado interno.

.
Teniendo esto en cuenta, los elementos de los formularios, naturalmente contienen un value o mejor dicho, mantienen sus propios estados y acciones dependiendo la interacción del usuario.
.
Y como ya sabemos, React realiza eso mismo para sus componentes con useState y solo los modifica con setState. Entonces para buscar una hegemonía entre los dos posibles casos y evitar esa “pelea de control”, se enlaza la propiedad natural value con el propio estado de ese campo de formulario.
.
De esta forma es por eso que el profesor agrega: value={newTodoValue} en el textarea, para enlazarlos y que solo sea React quien se encargue de manejarlo.
.
Eso se llama “Componente controlado”. El profe ya comentó sobre esto en los principios del curso incluso dejo la misma lectura, ahora con más conocimiento de la Librería se comprende mejor el concepto.
.
ES de saber que incluso si no se pone value={newTodoValue} en el textarea, el estado sigue funcionando y se puede realizar lo mismo, pero no lo estaría controlando React solamente, sino que se autocontrola también el mismo campo en sí. Se busca evitar eso.

Interacción con el Teclado

⠀⠀
Me daba cringe que no pudiera interactuar con las teclas jaja.
⠀⠀
Experimentando y rompiendo cositas por ahí aprendí que podemos añadir el evento onKeyPress que al presionar una tecla podemos validar que si es Enter la tecla que presionamos realice las mismas acciones que onSubmit.
⠀⠀

  • charCode es el método que permite interactuar con el código de los botones del teclado.
  • El código de Enter es 13
  • e -> Es simplemente el evento que estamos escuchando
    ⠀⠀
  const onKeyUp = (e) => {
    if (e.charCode === 13) {
      e.preventDefault();
      addTodo(newTodoValue);
      onCancel();
    }
  };

Si el charCode que escuchamos es igual a 13 entonces…

<form onSubmit={onSubmit} onKeyPress={onKeyUp}>

La App desde la vista principal:

El modal:

Mi paleta de colores:

Así va quedando la aplicación. Feedback en los comentarios…


realice algunas modificaciones en el CSS:

Asi va hasta ahora, acepto muchas correciones jaja 😁

Asi va quedando el proyecto!

Resultado Final:

Repo

TODO Web

La verdad estoy bastante conforme con el resultado

Para que se haga la validación sola de campo requerido solo necesitan colocar required dentro del tag del input o textarea

así

<textarea
                    value={newTodoValue}
                    onChange={onChange}
                    placeholder="Agrega un TODO"
                    required
                />

asi va la mia hehehe despues de 5452545 dias

Hola hola a todos, con ++ imr ++ se realiza la importación de React sin necesidad de escribir todo, y si no lo sabias…ahora lo sabes!! 😛

PD: uso las extensiones:
ES7+ React/Redux/React-Native snippets ( https://marketplace.visualstudio.com/items?itemName=dsznajder.es7-react-js-snippets)
Simple React Snippets (https://marketplace.visualstudio.com/items?itemName=burkeholland.simple-react-snippets)

Saludos colegas!!

Así quedó mi proyecto:

Este es mi proyecto finalizado:

así va quedando:

Si os fijáis cuando escribís una tarea si pulsáis intro para darle espacios estos no se respetan cuando se añaden a la lista de tareas.
Para ello solo tenéis que añadir esta propiedad al css, en este caso al item donde se renderiza la tarea, no se si me explico si las lista es un <ul>, cada item es un <li> pues en css añadís:

li {
  white-space: pre-line;
}

Con ello conseguís este efecto

Solo se guardan los cambios de línea pero no las indentaciones.

Listo 😛

Pues hoy con mucho orgullo, les comento que aca quedo mi aplicacion.



Aca les muestro lo que realice para el formulario.

import React from 'react';
import { TodoContext } from '../Context';
import './TodoForm.css';

function TodoForm(){

    const {addTodo, setOpenModal} = React.useContext(TodoContext);
    const [newTodoValue, setNewTodoValue] = React.useState('');

    const onCancel = () =>{
        setOpenModal(false);
    };

    const onSubmit = (event) => {
        event.preventDefault();
        addTodo(newTodoValue);
        setOpenModal(false);
    };

    const onChange = (event) => {
        setNewTodoValue(event.target.value);
    };
    
    return (
        <form onSubmit={onSubmit} className="TodoForm">
            <p><label>Escribe tu nuevo TODO</label></p>
            <textarea value={newTodoValue} onChange={onChange} placeholder='Cortar la Cebolla para el almuerzo' className='todoTXA' required  />
            <p className='botones'>
                {/* ?: preguntar si existe el dato en el arreglo, antes de imprimir el dato text */}
                <button type='button' className='exitModal' onClick={onCancel}>Cancelar</button>
                <button type='submit' className='addModal' >Añadir</button>
            </p>
        </form>
    );
}

export{TodoForm};

Hay algunas cosas ya las habia realizado pero ya quedo con todo, y listo para lo siguiente.

Agregue una validacion para que no se puedan agregar TODOs vacios o duplicados, aqui les dejo como va quedando mi aplicacion y el codigo de la validacion:

const {
    addTodo,
    setOpenModal,
    todo // llame la propiedad que contenia todos los TODOs
  } = React.useContext(TodoContext);
// hice una funcion para que recorriera cada TODO y devuelva true si el TODO a agregar ya existe
  const alreadyExist = () => {
    for (let i = 0; i < todo.length; i++) {
      if (todo[i].text === newTodo) {
        return true;
      }
    }
  }
// Agregue la validacion a la funcion onSubmit
const onSubmit = (event) => {
    event.preventDefault();
    if (newTodo.length <= 0 || alreadyExist()) {
      alert("No puedes agregar TODOs vacios o duplicados.");
    } else {
      addTodo(newTodo);
      setOpenModal(false);
    }
  };

Work in progress

https://github.com/Holmanyiyos/todo-app Los invito a revisar mi repositorio, ahí este proyecto cuenta con las siguientes mejoras:

  1. Tiene un loader cuando se realiza el setTimeout
  2. Recibe además del texto, la prioridad de esta
  3. Cuenta con 2 opciones de ordenamiento, por mayor prioridad o por tareas completadas.
  4. Al dar click en una tarea abre un modal para poder editar: su estado, su prioridad y su texto.
  5. la key ahora no es por el texto, sino que tiene un generador de ids unicos.

Si tienen dudas al respecto pueden preguntarme, espero sea de ayuda.

React se vuelve loco cuando tiene dos todos con el mismo nombre jajaja

Yo hice una función adicional llamada click, el punto es que cuando el usuario haga click en cualquier parte de la pantalla que no sea el formulario, se cerrará el portal.
Esto me parece que es más cómodo para usuarios con dispositivos móviles que presionar al boton para regresar

function Modal({ children }) {
    const { setOpenModal } = React.useContext(TodoContext);

    function click(event) {
        if (event.target.className === "ModalBackground") {
            setOpenModal(false);
        }
    }

    return createPortal(
        <div className="ModalBackground" onClick={click}>
            <div className="ModalBackground-contenedor">
                <TodoForm></TodoForm>
            </div>
        </div>,
        document.getElementById("modal")
    );
}

Así hice el mio:

Asi quedo mi FORM para la aplicación de TODO

Aquí esta mi CSS para que el botón quede sobre la ventana modal:

z-index: 1;

Para deshabilitar el boton de añadir cuando no hay ningun texto escrito en <textarea> o cuando el texto dentro de <textarea> sea menor a X numero de caracteres se lo puede deshabiliar con el atributo disabled.
Retornado true o false segun la condicion dentro de las llaves.

        <button
          className="TodoForm-button TodoForm-button-add"
          type= "submit"
          disabled={newTodoValue.length < 4 ? true : false}
        > Añadir
	</button>

Queria practicar el useEffect hook, asi que hice una pequeña validacion para no agregar TODOs duplicados ni vacios, mostrando una pequeña alerta y que desapareciera despues de 3 segundos.
Resulta que funciona todo bien pero si yo cerraba el modal antes de que pasaran los 3 segundos, ese ‘efecto’ quedaba corriendo incluso cuando ya no necesitaba actualizar ese estado.
La consola me mostraba un error que menciona algo de errores de optimizacion, y usar un useEffect y useCallback con una cleanup function o algo asi.
.
Creo que esto no se mencionó en la clase de useEffect, y es que dentro del useEffect hook, podemos retornar una funcion que limpie o que aborte ese ‘efecto’.

.
Lo que hice fue lo siguiente, y me resolvio el error, pero aun tengo dudas de como funciona:

/** newTodoState es un estado que tengo declarado arriba */
useEffect(() => {
	let subscription = setTimeout(() => {
		setNewTodoState(' ');
	}, 3000);

	// cleanup function
	return () => clearTimeout(subscription);
}, [newTodoState]);

Entonces, este useEffect se ejecuta cuando newTodoState se actualiza.
Lo que no me queda claro es: ¿Cuando existe un error, esta funcion retornada es invocada?
.

Así quedo

Genial 💚

asi me quedo

Emoji de basurero: 🗑️

Este curso ha sido genial me gusta mucho como va progresando el proyecto, y la forma de enseñanza de Juan es bastante funcional, comparto como quedo mi formulario

  • Formulario

para evitar guardar tareas vacías

const onSubmit = (event) => {
event.preventDefault();
if(newTodoValue !== ‘’){
addTodo(newTodoValue);
setOpenModal(false);
}else{
alert(“Debe agregar la tarea”);
}
}

Hola! Intente solucionar el boton con un if, validando el estado del modal, y cambiandolo con setOpenModal, y no funciono, ahora estoy viendo la solucion, y es mucho mas facil, pero me gustaria saber porque no funciona? Ayuda!!!

Hice que su propia ventana modal tenga su botón close:

js

import ReactDOM from 'react-dom';
import '../css/Modal.css';

function Modal({ children, setOpenModal }) {
    return ReactDOM.createPortal(
        <div className="modal">
            <div className="close-modal" onClick={() => setOpenModal(false)}>
                X
            </div>
            <div>{children}</div>
        </div>,
        document.getElementById('modal'),
    );
}

export default Modal;import ReactDOM from 'react-dom';
import '../css/Modal.css';

function Modal({ children, setOpenModal }) {
    return ReactDOM.createPortal(
        <div className="modal">
            <div className="close-modal" onClick={() => setOpenModal(false)}>
                X
            </div>
            <div>{children}</div>
        </div>,
        document.getElementById('modal'),
    );
}

export default Modal;

.
css

.modal {
    background: #202329cc;
    position: fixed;
    top: -10px;
    left: -10px;
    right: -10px;
    bottom: -10px;
    display: flex;
    justify-content: center;
    align-items: center;
    color: white;
}

.close-modal {
    position: absolute;
    width: 22px;
    text-align: center;
    top: 0;
    right: 0;
    margin-right: 25px;
    margin-top: 25px;
    font-size: 18px;
    font-weight: bold;
    color: white;
    cursor: pointer;
    padding: 10px;
}

.close-modal:hover {
    background-color: darkgrey;
    border-radius: 50%;
}

Agregue una alerta cuando intentan crear un To Do vacio.

JS

import React from "react";
import { ToDoContext } from "../TodoContext";
import form from "./form.module.css"

function TodoForm() {

  const [newTodoValue, setNewTodoValue] = React.useState(''),
        [showAlert, setShowAlert] = React.useState(false);

  const {
    addTodo,
    setOpenModal
  } = React.useContext(ToDoContext);


  const onChange = (event) => {
    setNewTodoValue(event.target.value);
    setShowAlert(false)
  }
  const onCancel = () => {
    setOpenModal(false);
  }
  const onSubmit = (event) => {
    event.preventDefault();
    if(newTodoValue.length < 0) {
      addTodo(newTodoValue);
      onCancel();
    } else{
      setShowAlert(true)
    }
  }

  return (
    <form onSubmit={onSubmit}>
      <label>Crea tu nuevo TODO</label>
      <textarea
        value={newTodoValue}
        onChange={onChange}
        placeholder="Que tienes que hacer?"
      />
      {showAlert && <p className={form.alert}>Escribe el texto de tu nuevo To Do</p>}
      <div className={form.buttonContainer}>
        <button
          type="button"
          className={`${form.button} ${form['button--cancel']}`}
          onClick={onCancel}
        >
          Cancelar
        </button>
        <button
          type="submit"
          className={`${form.button} ${form['button--add']}`}
        >
          Añadir
        </button>
      </div>
    </form>
  );
}

export { TodoForm }

CSS

.alert {
  color: red;
  font-size: 14px;
  font-weight: bold;
  text-align: center;
  padding: 14px 0 0;
  margin: 0;
}

Prevenir la acción por defecto de enviar info + recargar pagina en un Submit Button

tuve que añadirle una validacion, cuando el texto este vacio que no agregue el todo.

const addTodo = (text) => {
    if (text !== '') {
      todos.push({
        completed: false,
        text,
      });
      const newTodos = [...todos];
      setTodos(newTodos);
    }
  }

Pueden capturar el evento onSubmit del formulario y guardar toda su data en un objeto de esta forma:

    const onSave = (event) => {
        event.preventDefault();
        // Capturamos la data en un objeto
        const data = Object.fromEntries(
            new FormData(event.target)
        )
        console.log(data);
    }

El formulario quedaría así:

        <form onSubmit={onSave}>
            <label></label>
            <textarea 
                placeholder="cortar la cebolla para el almuerzo"
                name="text"
            >

            </textarea>
            <div>
                <button
                type="button"
                    onClick={onCancel}
                >
                    Cancelar
                </button>
                <button
                type="submit">
                    Guardar
                </button>
            </div>
        </form>

Es importante que le coloquen el atributo name al textarea

Una pequeña modificación que me pareció útil:

De esa manera vamos a poner desmarcar una tarea en el caso de que necesitemos hacerlo 😄

El orden afecta ? Por ejemplo primero escribió el evento onclick pero despuès encima escribió type = "button "
¿ para que sirve el typebutton ?
Cómo puedo identificar el orden en que debo hacer los eventos ?

Aprender React es toda una aventura

Aun falta mejorar algunas partes con css, pero me gusta como esta quedando.

El evento onSubmit
Este evento se encarga de ejecutar un determinado código de javascript al realizarse el envío de un formulario. El ejemplo mas claro de utilización de este evento es evitar que un formulario sea enviado si determinadas condiciones no son cumplidas.

Así quedo mi aplicación:

mi resultado

Tengo este error, no me sale nada en el navegador😖
.
Investigue un poco y antes del: deleteTodo
.
Coloque la siguiente linea de código: /*eslint-disable-next-line no-unused-vars*/
.
Pero aun asi, no me funciono.

Bueno así quedó mi aplicación, le puse id auto incrementable, fecha de creación del TODO y fecha de finalización y por supuesto ya realice el arreglo del erro que sale en el contador, estaba al revés 🙈
![](

![](

Me ha gusta mucho el curso, sin embargo se me hace que los videos de las lecciones son muy largas, por lo regular prefiero que sean más clases pero de menos de 10 mins, es mi opinión.

Llegados este punto me gustaría compartirles mi aplicación con la integración del modal:

Hice una adición de un mensaje de error cuando el usuario intente ingresar una actividad vacía, el modal muestra un mensaje en rojo alertando de la acción. Para ello creé un nuevo estado local dentro del formulario

const [emptyTaskValidation, setEmptyTaskValidation] = React.useState(false);

Y dentro de los métodos de change y submit realicé la validación cuando la entrada de texto está vacía para dos escenarios:

  • Cuando el usuario recién abre el modal y lo intenta enviar vacío de una vez
  • Cuando el usuario ingresa texto y después lo elimina dejando el textarea vacío
const handleChange = (e) => {
	if (e.target.value.length == 0) {
		setEmptyTaskValidation(true);
	} else {
		setEmptyTaskValidation(false);
	}
	setNewTaskValue(e.target.value);
} 

const onSubmit = (e) => {
	e.preventDefault();
	if (!emptyTaskValidation && newTaskValue.length != 0){
		createTask(newTaskValue);
		setOpenModal(false);
	} else {
		setEmptyTaskValidation(true);
	}
}

Por ahora ese diseño aplica únicamente para interfaces mobile, pero mi idea es después hacer un diseño de interfaz para desktop que muestre el modal en pantalla sin acceder al botón!

Asi va quedando!!

<code> function CreateTodoButton(props) {
  const {openModal} = React.useContext(TodoContext);
  const clickButton = () => {
    props.setOpenModal(!openModal);
  }

  return (
    <button className="CreateTodoButton"
      onClick= { () => clickButton()} 
    >
      +
    </button> 

  );
}
export {CreateTodoButton};

Falto algo muy importante, en el textarea si dejamos el campo vacio se crea una tarea vacia. Para evitar eso debemos agregar un ‘required’ al textarea.

      <textarea
        required
        value={newTodoValue}
        onChange={onWrite}
        placeholder="Tomar agua"
      />

De esta manera le indica al usuario que no puede crear una tarea vacia y evitamos ese error.

¿Cómo no romper la UI con el textarea?
Si tratan de agrandar el textarea, los usuarios podrían llegar a desbordar todo el textarea, por eso les recomiendo agregar un resize:vertical, y un min-height y max-height con los valores que deseen.

Yo lo puse así:

textarea {
    background-color: #F9FBFC;
    border: 2px, solid #202329;
    border-radius: 2px;
    box-shadow: 0px 5px 50px rgba(32, 35, 41, 0.25);
    color: #1E1E1F;
    font-size: 20px;
    text-align: center;
    padding: 12px;
    min-height: 96px;
    max-height: 200px;
    width: calc(100%-25px);
    resize: vertical;
} 

A la clase .CreateTodoButton le agregue un z-index
y luego pase la propiedad openModal al componente CreateTodoButton y la funcion onClickButon quedo asi:

<code>
const onClickButton = (msg) => {
    props.setOpenModal(!props.openModal);
  };

Lo hice de mi equipo de futbol favorito, y los fichajes que quiere hacer este verano XD
La pantalla principal:


Y la pantalla de agregar fichaje:

Hola,

.

El main:

El modal:

Lo que llevo:

Modal:

Así quedó mi app 😊

tengo un problema realizando este ejercicio y en lugar de venir a la comunidad he dejado pasar el tiempo. luego veo mi error y me doy cuenta que no estoy poniendo tanta atencion y estoy apurado por aprender, como ser paciente contigo mismo? ire al curso de inteligenccia emocional despues de terminar este

Dos funcionalidades que le añadiría:

  • Editar TODOs.
  • Evitar que se añadan TODOs vacíos.

Les dejo unos estilitos para el button cuando está abierto el modal. Solo para que cambie de fondo y shadow 😄

.closeTodoButton {
  transform: rotate(45deg);
  background-color: #fa3a3a;
  box-shadow: 0px 5px 25px rgba(250, 58, 58, 0.5);
}

.CreateTodoButton:hover {
  transform: rotate(180deg);
}
import React from "react";
import './CreateTodoButton.css';

function CreateTodoButton({setOpenModal, openModal}) {
    const onClickButton = () => {
        setOpenModal(!openModal);
    }
    return (
        <button
         className={`CreateTodoButton ${openModal && 'closeTodoButton'}`}
         onClick={onClickButton}
         >+</button>
    );
}

export {CreateTodoButton};

Si ingreso un todo repetido, lanza error porque ambos tienen la misma key. No sé si alguien ha presentado el mismo error.

Pueden colocar required en el textarea para aprovechar las validaciones por html y evitar inputs vacios en el textarea

      <textarea 
        placeholder='Cortar la Cebolla'
        value={newTodoValue}
        onChange={onChange}
        required
      />

Igualmente valerse de la propiedad disabled en el botón de agregar para validar que el textarea esté completo

        <button
          type='submit'
          disabled={!newTodoValue && true}
        >
          Agregar
        </button>

También en el método onSubmit validar que el newTodoValue exista

  const onSubmit = (event) => {
    event.preventDefault()
    newTodoValue && addTodo(newTodoValue)
    setOpenModal(false)
  }

Son varias maneras de abordar el mismo asunto

Le di mis propios estilos y cambié algunas cositas, así vamos hasta ahora!

Así va quedando mi aplicación, le agregue un botón para cambiar el color.

Adicional dejo ideas para que sea aún más profesional el proyecto:

  1. Crear componente de carga
  2. Enviar la prioridad con los checkbox
  3. Ordenar tareas según prioridad
  4. Apartado de editar tarea, prioridad texto etc
  5. Crear listas para personal, trabajo otros y que sean customisables

wow que bien quedo la aplicacion, me gusto el orden y abstraccion de este proyecto. codigo muy legible

Así me quedó. utilizando componentes de MUI

Pero el botón de agregar tareas no tiene sentido porque los botones “Cancelar” y “añadir” ya cumplen su función, para la versión final sería un error dejar ese botón ahí

Mis estilos con algunas modificaciones:

function CreateTodoButton() {
  const { openModal, setOpenModal } = React.useContext(TodoContext);

  const onClickBtn = () => {
    setOpenModal(!openModal);
  };

  return (
    <button
      className={`CreateTodoButton ${openModal && ("CloseTodoButton")}`}
      onClick={() => {
        onClickBtn();
      }}
    >
      +
    </button>
  );
}

export { CreateTodoButton };

y css

.CreateTodoButton {
    background-color: #61DAFA;
    box-shadow: 0px 5px 25px rgba(97, 218, 250, 0.5);
    border: none;
    border-radius: 50%;
    cursor: pointer;
    font-size: 50px;
    position: fixed;
    bottom: 24px;
    right: 24px;
    font-weight: bold;
    color: #FAFAFA;
    display: flex;
    justify-content: center;
    align-items: center;
    height: 64px;
    width: 64px;
    transform: rotate(0);
    transition: .3s ease;
    z-index: 1;
}

.CloseTodoButton {
    transform: rotate(224deg);
}

Me di a la tarea de evitar que se agregara algún todo si el textarea no tenia ninguna información. Añadí la siguiente condición en la función onSubmit

    const onSubmit = (event) => {
        event.preventDefault();

        if (!newTodoValue) {
            return null
        }
        addTodo(newTodoValue);
        setOpenModal(prevState => !prevState)
    };

Suave diria yo

Para los que quieran agregar el efecto de venir de arriba al modal, pueden agregar el siguiente css. la clase “modal-container” y la clase “a-open-modal” se agrega si el modal está abierto.

.modal-container {
    background-color: #242526;
    width: 500px;
    padding: 20px;
    border-radius: 5px;
    position: relative;
}

.a-open-modal {
    animation: mymove 0.5s;
}

@keyframes mymove {
    0%   {top: -50px;}
    100% {top: 0px;}
}

Esta fue mi solución para el reto anterior.
Solo agregue la propiedad de CSS z-index a la clase “CreateToDoButton” como puede verse a continuación.

.CreateToDoButton {
	z-index: 9;
}

Y en cuanto a la validación que utilice en el método que utiliza el botón del modal, solo agregue lo siguiente utilizando las props de React.

const onClickButton = () => {
        (props.openModal) ? props.setOpenModal(false) : props.setOpenModal(true);
    }

Saludos!

Yo que había solucionado el reto con un simple if ternario. 😭

Yo habia solucionado el reto de esta manera en el js

Despues de muchas otras implementaciones, a quienes no les funciono el modal, nunca cree otro root en mi index, React es una biblioteca single page y ps es de buenas practicas solo desembocar en un componente, como alternativa lo que hice fue crear el estado del modal en App.js, el cual por defecto esta en false y solo cuando accionan el + se abre el modal

const [modal, setModal] = React.useState(false);
<button className="form-control bg-dark my-1 addTodo" onClick={() => setModal(true)}><FaPlus/></button>

, y se cierra el modal al momento de hacer el clic al boton agregar el nuevo elemento

  const addTodo = () => {
        props.newTodo(tarea)
        props.modal(false)
    }

<button onClick={addTodo} className="btn btn-info my-3">Add</button>

App.js

<Boton 
          newTodo={(tarea) => addTodo(tarea)} 
          modal={(close) => setModal(close)}
          /> :

tambien importante nunca actualizar el estado todos directamente agregando el nuevo elemento, como esto: [...todos,newTodo] , mejor crea un nuevo array que recolecte los todos que van hasta el momento y agrega el nuevo elemento a la lista nueva, y esa lista nueva sea la que actualize el estado por completo.

 const addTodo = (tarea) => {
    let addTask = [...todos]
    addTask.push(tarea)
    setTodos(addTask)
  }

De esta manera logre hacer el toogle, tambien me traje el estado del modal para cambiar el estilo del CreateTodoButton en funcion de si el modal esta abierto o cerrado

const CreateTodoButton = ({ openModal, setOpenModal }) => {
  const toggleModal = () => {
    openModal ? setOpenModal(false) : setOpenModal(true);
  };

  return (
    <button
      className={openModal ? "create-button delete-button" : "create-button"}
      onClick={toggleModal}
    >
      +
    </button>
  );
};

Cuando el modal esta cerrado:

Cuando el modal esta abierto:

La funcionalidad adicional que sentí que era necesaria era poder desmarcar como completados los TODOs, entonces cambié la funcion completeTODO

        newTodos[todoIndex].completed = !newTodos[todoIndex].completed;

Prevenir la acción por defecto de enviar info + recargar pagina en un Submit Button

Da la impresion que las clases se graban “sobre la marcha”, se pierde mucho tiempo en corregir errores distrayendo del aprendizaje. Creo que puede haber una mejor forma de “enseñar sobre errores” que cometerlos en vivo, forzándonos a tambien cometerlos si estamos replicando lo que el profesor hace.

Hice que no puedas hacer todos repetidos c:<
Si se quieren ver un poco el codigo aqui el repositorio: https://github.com/GGGamesXDlol/todomachine/tree/master/src
Y un video de demostración de como funciona:
https://youtu.be/zRcdzNNO7VQ

Para quienes no hayan usado el CSS de JuanDC en el button, tengan en cuenta que para utilizar

z-index: 1;

el elemento debe tener un position que no sea static. Pueden ponerle relative o absolute como el profe. Fíjense, qué les conviene en base a su diseño.

Inentedible como esta explicado.