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 鈥減elea 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 鈥淐omponente 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鈥hora 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 鈥榚fecto鈥 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 鈥榚fecto鈥.

.
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(鈥淒ebe 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 鈥榬equired鈥 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 鈥淐ancelar鈥 y 鈥渁帽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 鈥渕odal-container鈥 y la clase 鈥渁-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 鈥淐reateToDoButton鈥 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 鈥渟obre la marcha鈥, se pierde mucho tiempo en corregir errores distrayendo del aprendizaje. Creo que puede haber una mejor forma de 鈥渆nse帽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.