CursosEmpresasBlogLiveConfPrecios

Reto: loading skeletons

Clase 20 de 23 • Curso de React 17

Contenido del curso

Primeros pasos con React

  • 1
    Cómo aprender React.js

    Cómo aprender React.js

    08:19 min
  • 2
    Cuándo usar React.js

    Cuándo usar React.js

    13:27 min
  • 3
    Cambios en React 18: ReactDOM.createRoot

    Cambios en React 18: ReactDOM.createRoot

    01:21 min
  • 4
    Instalación con Create React App

    Instalación con Create React App

    19:13 min

Fundamentos de React: maquetación

  • 5
    Componentes de TODO Machine

    Componentes de TODO Machine

    20:31 min
  • 6
    JSX: componentes vs. elementos (y props vs. atributos)

    JSX: componentes vs. elementos (y props vs. atributos)

    16:15 min
  • 7
    CSS en React

    CSS en React

    17:54 min

Fundamentos de React: interacción

  • 8
    Manejo de eventos

    Manejo de eventos

    14:40 min
  • 9
    Manejo del estado

    Manejo del estado

    12:50 min
  • 10
    Contando y buscando TODOs

    Contando y buscando TODOs

    21:16 min
  • 11
    Completando y eliminando TODOs

    Completando y eliminando TODOs

    13:34 min

Fundamentos de React: escalabilidad

  • 12
    Organización de archivos y carpetas

    Organización de archivos y carpetas

    16:08 min
  • 13
    Persistencia de datos con Local Storage

    Persistencia de datos con Local Storage

    16:16 min
  • 14
    Custom Hook para Local Storage

    Custom Hook para Local Storage

    12:45 min
  • 15
    Manejo de efectos

    Manejo de efectos

    20:37 min
  • 16
    React Context: estado compartido

    React Context: estado compartido

    16:48 min
  • 17
    useContext

    useContext

    07:10 min

Modales y formularios

  • 18
    Portales: teletransportación de componentes

    Portales: teletransportación de componentes

    15:32 min
  • 19
    Formulario para crear TODOs

    Formulario para crear TODOs

    19:57 min

Retos

  • 20
    Reto: loading skeletons

    Reto: loading skeletons

    Viendo ahora
  • 21
    Reto: icon component

    Reto: icon component

    03:51 min

Próximos pasos

  • 22
    Deploy con GitHub Pages

    Deploy con GitHub Pages

    09:49 min
  • 23
    Toma el Curso de React.js: Patrones de Render y Composición

    Toma el Curso de React.js: Patrones de Render y Composición

    01:37 min
Tomar examen

Escuelas

  • Desarrollo Web
    • Fundamentos del Desarrollo Web Profesional
    • Diseño y Desarrollo Frontend
    • Desarrollo Frontend con JavaScript
    • Desarrollo Frontend con Vue.js
    • Desarrollo Frontend con Angular
    • Desarrollo Frontend con React.js
    • Desarrollo Backend con Node.js
    • Desarrollo Backend con Python
    • Desarrollo Backend con Java
    • Desarrollo Backend con PHP
    • Desarrollo Backend con Ruby
    • Bases de Datos para Web
    • Seguridad Web & API
    • Testing Automatizado y QA para Web
    • Arquitecturas Web Modernas y Escalabilidad
    • DevOps y Cloud para Desarrolladores Web
  • English Academy
    • Inglés Básico A1
    • Inglés Básico A2
    • Inglés Intermedio B1
    • Inglés Intermedio Alto B2
    • Inglés Avanzado C1
    • Inglés para Propósitos Específicos
    • Inglés de Negocios
  • Marketing Digital
    • Fundamentos de Marketing Digital
    • Marketing de Contenidos y Redacción Persuasiva
    • SEO y Posicionamiento Web
    • Social Media Marketing y Community Management
    • Publicidad Digital y Paid Media
    • Analítica Digital y Optimización (CRO)
    • Estrategia de Marketing y Growth
    • Marketing de Marca y Comunicación Estratégica
    • Marketing para E-commerce
    • Marketing B2B
    • Inteligencia Artificial Aplicada al Marketing
    • Automatización del Marketing
    • Marca Personal y Marketing Freelance
    • Ventas y Experiencia del Cliente
    • Creación de Contenido para Redes Sociales
  • Inteligencia Artificial y Data Science
    • Fundamentos de Data Science y AI
    • Análisis y Visualización de Datos
    • Machine Learning y Deep Learning
    • Data Engineer
    • Inteligencia Artificial para la Productividad
    • Desarrollo de Aplicaciones con IA
    • AI Software Engineer
  • Ciberseguridad
    • Fundamentos de Ciberseguridad
    • Hacking Ético y Pentesting (Red Team)
    • Análisis de Malware e Ingeniería Forense
    • Seguridad Defensiva y Cumplimiento (Blue Team)
    • Ciberseguridad Estratégica
  • Liderazgo y Habilidades Blandas
    • Fundamentos de Habilidades Profesionales
    • Liderazgo y Gestión de Equipos
    • Comunicación Avanzada y Oratoria
    • Negociación y Resolución de Conflictos
    • Inteligencia Emocional y Autogestión
    • Productividad y Herramientas Digitales
    • Gestión de Proyectos y Metodologías Ágiles
    • Desarrollo de Carrera y Marca Personal
    • Diversidad, Inclusión y Entorno Laboral Saludable
    • Filosofía y Estrategia para Líderes
  • Diseño de Producto y UX
    • Fundamentos de Diseño UX/UI
    • Investigación de Usuarios (UX Research)
    • Arquitectura de Información y Usabilidad
    • Diseño de Interfaces y Prototipado (UI Design)
    • Sistemas de Diseño y DesignOps
    • Redacción UX (UX Writing)
    • Creatividad e Innovación en Diseño
    • Diseño Accesible e Inclusivo
    • Diseño Asistido por Inteligencia Artificial
    • Gestión de Producto y Liderazgo en Diseño
    • Diseño de Interacciones Emergentes (VUI/VR)
    • Desarrollo Web para Diseñadores
    • Diseño y Prototipado No-Code
  • Contenido Audiovisual
    • Fundamentos de Producción Audiovisual
    • Producción de Video para Plataformas Digitales
    • Producción de Audio y Podcast
    • Fotografía y Diseño Gráfico para Contenido Digital
    • Motion Graphics y Animación
    • Contenido Interactivo y Realidad Aumentada
    • Estrategia, Marketing y Monetización de Contenidos
  • Desarrollo Móvil
    • Fundamentos de Desarrollo Móvil
    • Desarrollo Nativo Android con Kotlin
    • Desarrollo Nativo iOS con Swift
    • Desarrollo Multiplataforma con React Native
    • Desarrollo Multiplataforma con Flutter
    • Arquitectura y Patrones de Diseño Móvil
    • Integración de APIs y Persistencia Móvil
    • Testing y Despliegue en Móvil
    • Diseño UX/UI para Móviles
  • Diseño Gráfico y Arte Digital
    • Fundamentos del Diseño Gráfico y Digital
    • Diseño de Identidad Visual y Branding
    • Ilustración Digital y Arte Conceptual
    • Diseño Editorial y de Empaques
    • Motion Graphics y Animación 3D
    • Diseño Gráfico Asistido por Inteligencia Artificial
    • Creatividad e Innovación en Diseño
  • Programación
    • Fundamentos de Programación e Ingeniería de Software
    • Herramientas de IA para el trabajo
    • Matemáticas para Programación
    • Programación con Python
    • Programación con JavaScript
    • Programación con TypeScript
    • Programación Orientada a Objetos con Java
    • Desarrollo con C# y .NET
    • Programación con PHP
    • Programación con Go y Rust
    • Programación Móvil con Swift y Kotlin
    • Programación con C y C++
    • Administración Básica de Servidores Linux
  • Negocios
    • Fundamentos de Negocios y Emprendimiento
    • Estrategia y Crecimiento Empresarial
    • Finanzas Personales y Corporativas
    • Inversión en Mercados Financieros
    • Ventas, CRM y Experiencia del Cliente
    • Operaciones, Logística y E-commerce
    • Gestión de Proyectos y Metodologías Ágiles
    • Aspectos Legales y Cumplimiento
    • Habilidades Directivas y Crecimiento Profesional
    • Diversidad e Inclusión en el Entorno Laboral
    • Herramientas Digitales y Automatización para Negocios
  • Blockchain y Web3
    • Fundamentos de Blockchain y Web3
    • Desarrollo de Smart Contracts y dApps
    • Finanzas Descentralizadas (DeFi)
    • NFTs y Economía de Creadores
    • Seguridad Blockchain
    • Ecosistemas Blockchain Alternativos (No-EVM)
    • Producto, Marketing y Legal en Web3
  • Recursos Humanos
    • Fundamentos y Cultura Organizacional en RRHH
    • Atracción y Selección de Talento
    • Cultura y Employee Experience
    • Gestión y Desarrollo de Talento
    • Desarrollo y Evaluación de Liderazgo
    • Diversidad, Equidad e Inclusión
    • AI y Automatización en Recursos Humanos
    • Tecnología y Automatización en RRHH
  • Finanzas e Inversiones
    • Fundamentos de Finanzas Personales y Corporativas
    • Análisis y Valoración Financiera
    • Inversión y Mercados de Capitales
    • Finanzas Descentralizadas (DeFi) y Criptoactivos
    • Finanzas y Estrategia para Startups
    • Inteligencia Artificial Aplicada a Finanzas
    • Domina Excel
    • Financial Analyst
    • Conseguir trabajo en Finanzas e Inversiones
  • Startups
    • Fundamentos y Validación de Ideas
    • Estrategia de Negocio y Product-Market Fit
    • Desarrollo de Producto y Operaciones Lean
    • Finanzas, Legal y Fundraising
    • Marketing, Ventas y Growth para Startups
    • Cultura, Talento y Liderazgo
    • Finanzas y Operaciones en Ecommerce
    • Startups Web3 y Blockchain
    • Startups con Impacto Social
    • Expansión y Ecosistema Startup
  • Cloud Computing y DevOps
    • Fundamentos de Cloud y DevOps
    • Administración de Servidores Linux
    • Contenerización y Orquestación
    • Infraestructura como Código (IaC) y CI/CD
    • Amazon Web Services
    • Microsoft Azure
    • Serverless y Observabilidad
    • Certificaciones Cloud (Preparación)
    • Plataforma Cloud GCP

Platzi y comunidad

  • Platzi Business
  • Live Classes
  • Lanzamientos
  • Executive Program
  • Trabaja con nosotros
  • Podcast

Recursos

  • Manual de Marca

Soporte

  • Preguntas Frecuentes
  • Contáctanos

Legal

  • Términos y Condiciones
  • Privacidad
  • Tyc promociones
Reconocimientos
Reconocimientos
Logo reconocimientoTop 40 Mejores EdTech del mundo · 2024
Logo reconocimientoPrimera Startup Latina admitida en YC · 2014
Logo reconocimientoPrimera Startup EdTech · 2018
Logo reconocimientoCEO Ganador Medalla por la Educación T4 & HP · 2024
Logo reconocimientoCEO Mejor Emprendedor del año · 2024
De LATAM conpara el mundo
YoutubeInstagramLinkedInTikTokFacebookX (Twitter)Threads

      Muchas aplicaciones se quedan en blanco mientras cargan su contenido. Otras muestran algún mensaje de "cargando" y luego sí renderizan todo el contenido de la aplicación.

      Pero para ofrecerle una mejor experiencia a nuestros usuarios (UX) es mejor renderizar todo el contenido posible, incluso si no ha terminado de cargar alguna parte de la aplicación. En TODO Machine, por ejemplo, podemos mostrar varios componentes desde el principio aunque no hayamos terminado de cargar la lista de TODOs.

      Pero un párrafo que diga "cargando" definitivamente NO es la mejor forma de comunicarle a los usuarios que estamos cargando (el mensaje es claro, pero podríamos buscar una solución más... estética).

      Existen muchas posibles soluciones, desde animaciones sencillas (como 3 puntitos intercalando diferentes niveles de opacidad) hasta loading skeletons (esqueletos de carga). Incluso existen herramientas interactivas como Create React Content Loader para agilizar estos desarrollos.

      El reto de esta clase es que maquetes cualquiera de estas soluciones y nos la muestres en la sección de comentarios. En esta lectura te mostraré mi solución, pero te recomiendo que no la veas hasta intentar tu propia solución.

      :bulb: Te recomiendo esta lectura: 5 estados clave para crear interfaces coherentes

      Tu propio loading skeleton en React

      Lo primero que vamos a hacer es crear 3 nuevos componentes para trabajarlos independientemente: TodosError, TodosLoading y EmptyTodos.

      carbon.png

      Ya que tenemos estos 3 componentes, ahora vamos a llamarlos desde el componente AppUI para conectarlos con la aplicación.

      carbon (1).png

      ¡Muy bien! Ahora sí podemos concentrarnos mucho mejor para trabajar el estado de carga de TODOS dentro del componente TodosLoading.

      Para empezar, voy a crear y conectar un archivo TodosLoading.css para definir los estilos de mi esqueleto:

      carbon (2).png

      Llegó el momento más importante: maquetar.

      Debemos definir qué elementos necesitamos para el esqueleto y luego les daremos estilos con CSS. Como la idea es replicar la estructura de un TODO, vamos a necesitar una cajita para el contenedor del TODO, una cajita para el ícono de completar, otra cajita para el ícono de borrar y una última cajita para el texto.

      :bulb: Le digo a cada elemento "cajita" porque el objetivo de los esqueletos de carga no es replicar todos estos elementos, sino que cada uno será un elemento "fantasma". Deben ser lo suficientemente parecidos a los TODOs reales para que los usuarios entiendan que estos elementos están relacionados con los TODOs, pero lo suficientemente abstractos y grisáceos para que se entienda que aún los estamos cargando.

      carbon (3).png

      ¡Y ahora debemos crear los estilos!

      Primero vamos a definir los tamaños y posiciones de cada elemento (tal cual copiando y pegando los estilos del TodoItem.css, pero cambiando los nombres de las clases y descartando las propiedades innecesarias):

      carbon (4).png

      Ahora vamos a todas las cajitas (menos la del texto) para darles un color de fondo con gradiente:

      carbon (5).png

      Luego le configuraremos un tamaño de fondo lo suficientemente grande como para que pueda darla vuelta sin dejar espacios vacíos (400% es más que suficiente):

      carbon (6).png

      Y finalmente le daremos una animación que cambie la posición del fondo al principio, a la mitad y al final (te recomiendo darle al menos 3 segundos de duración para que tu animación no se vea atropellada, sino por el contrario con un efecto suave e hipnotizante):

      carbon (7).png

      :bulb: Puedes ir a useLocalStorage.js y cambiar el tiempo que tardamos en llamar a nuestro efecto con la función setTimeout para poder visualizar tu esqueleto de carga correctamente.

      Recuerda que puedes tomar esta trilogía de cursos para aprender muchísimo más sobre transformaciones, transiciones y animaciones con CSS:

      Curso de Transformaciones y Transiciones con CSS Curso de Animaciones con CSS Curso Práctico de Maquetación y Animaciones con CSS

      ¡Te espero en la siguiente clase para un nuevo reto!

      👉 Aquí puedes encontrar el repositorio de este reto: Clase bonus: loading skeleton

      Comentarios

      Obed Paz

      Obed Paz

      student•
      hace 5 años
        Luis Alejandro Vera Hernandez

        Luis Alejandro Vera Hernandez

        student•
        hace 4 años
        Cristian David Contreras López

        Cristian David Contreras López

        student•
        hace 4 años
      Juan Sebastián Poveda Florez

      Juan Sebastián Poveda Florez

      student•
      hace 4 años
        Mauricio Gonzalez Falcon

        Mauricio Gonzalez Falcon

        student•
        hace 3 años
        Marco Antonio Alducin Garcia

        Marco Antonio Alducin Garcia

        student•
        hace 3 años
      Cristian David Rojas Carvajal

      Cristian David Rojas Carvajal

      student•
      hace 5 años
        Juan Castro

        Juan Castro

        teacher•
        hace 5 años
        Francisco Israel Jimenez Ramirez

        Francisco Israel Jimenez Ramirez

        student•
        hace 5 años
      Rodrigo Milesi

      Rodrigo Milesi

      student•
      hace 5 años
        Nazareno Aznar Altamiranda

        Nazareno Aznar Altamiranda

        student•
        hace 5 años
      Gustavo Gonzalez Montero

      Gustavo Gonzalez Montero

      student•
      hace 4 años
        Gustavo Gonzalez Montero

        Gustavo Gonzalez Montero

        student•
        hace 4 años
      Angel Choque

      Angel Choque

      student•
      hace 5 años
        Stiven Trujillo

        Stiven Trujillo

        student•
        hace 4 años
        Mauricio Chávez

        Mauricio Chávez

        student•
        hace 4 años
      John Orellana

      John Orellana

      student•
      hace 4 años
        Lean Silva

        Lean Silva

        student•
        hace 3 años
      Gustavo A. González G.

      Gustavo A. González G.

      student•
      hace 4 años
        Juan Castro

        Juan Castro

        teacher•
        hace 4 años
        Alejandro Forero Vanegas

        Alejandro Forero Vanegas

        student•
        hace 4 años
      Juan Fernando Yepes Muñoz

      Juan Fernando Yepes Muñoz

      student•
      hace 4 años
      Raul Wabe

      Raul Wabe

      student•
      hace 4 años
      Alan Dromundo Arias

      Alan Dromundo Arias

      student•
      hace 3 años
      Kevin Andres García Velez

      Kevin Andres García Velez

      student•
      hace 4 años
        Lean Silva

        Lean Silva

        student•
        hace 3 años
      Luis Felipe Silgado Cortázar

      Luis Felipe Silgado Cortázar

      student•
      hace 5 años
      Javier Alejandro Albornoz Pérez

      Javier Alejandro Albornoz Pérez

      student•
      hace 4 años
        Juan Castro

        Juan Castro

        teacher•
        hace 4 años
      Samuel Montoya Gallo

      Samuel Montoya Gallo

      student•
      hace 3 años
      Paula Inés Cudicio

      Paula Inés Cudicio

      student•
      hace 3 años
      CHRISTIAN OLIVER SOLANO NUÑEZ

      CHRISTIAN OLIVER SOLANO NUÑEZ

      student•
      hace 4 años
        Juan Castro

        Juan Castro

        teacher•
        hace 4 años
      Miguel Angel Armenta Acosta

      Miguel Angel Armenta Acosta

      student•
      hace 4 años
      David Rodriguez

      David Rodriguez

      student•
      hace 4 años
      ZANONI ALFREDO SALAS TOBÓN

      ZANONI ALFREDO SALAS TOBÓN

      student•
      hace 4 años

      Loading and Empty state screens: .

      . Error State Screen: .

      screenshootTodoError.png

      Me gusta mucho! :D

      Bro, wow, te mamaste te quedo genial.

      Opte por los clásicos punticos de carga

      Tu diseño de todo menos clásico, Te quedo genial

      Me gusta mucho tu animacion.

      Hola 😀

      Para que nos muestre varios componentes TodosLoading podemos hacer esto

      {loading && new Array(5).fill(1).map((a, i) => <TodosLoading key={i} />)}

      resultado

      Loading.png

      Fino

      hola, gracias por el aporte!!, justo me estaba preguntando como colocar varios sin tener que repetir la maquetación hardcodeada por así decirlo haha !

      Sin título.jpg
      asi me quedo! me parece una buena solución !

      {loading && new Array(4).fill().map((item, index)=>( <LoadingTodo key={index} /> ))}
      const LoadingTodo = () => { return ( <li className="TodoItem-loading"> <div className="LoaderBalls"> <span className="LoaderBalls__item"></span> <span className="LoaderBalls__item"></span> <span className="LoaderBalls__item"></span> </div> </li> ) }
      .TodoItem-loading { background-color: #FAFAFA; position: relative; display: flex; justify-content: center; align-items: center; margin-top: 24px; box-shadow: 0px 5px 50px rgba(32, 35, 41, 0.15); color: #333; min-height: 4.5rem; } .LoaderBalls { width: 90px; display: flex; justify-content: space-between; align-items: center; } .LoaderBalls__item { width: 20px; height: 20px; border-radius: 50%; background: #bec3c5; } .LoaderBalls__item:nth-child(2) { animation: opacitychange 1s ease-in-out infinite; } .LoaderBalls__item:nth-child(3) { animation: opacitychange 1s ease-in-out 0.33s infinite; } .LoaderBalls__item:nth-child(1) { animation: opacitychange 1s ease-in-out 0.66s infinite } @keyframes opacitychange { 0%, 100% { opacity: 0; } 60% { opacity: 1; } }
      localhost_3000_(Moto G4) (2).png

      Genial la solucion para repetir los todos, no lograba dar con eso ¡Gracias!

      Loading state:

      Loading-State.png

      Ideal state:

      Ideal-State.png

      Error state

      Error-State.png

      Empty state:

      Empty-State.png

      All completed state:

      AllCompleted-State.png

      TodoForm

      Todo-Form.png

      Loading code:

      //TodoLoading.js import React from "react" import ContentLoader from "react-content-loader" import './TodoLoad.css' const TodoLoad = (props) => ( <ContentLoader speed={2} width={280} height={40} viewBox="0 0 280 40" backgroundColor="#3d087b" foregroundColor="#f43bea" {...props} > <rect x="48" y="8" rx="3" ry="3" width="88" height="6" /> <rect x="48" y="26" rx="3" ry="3" width="52" height="6" /> <circle cx="20" cy="20" r="20" /> </ContentLoader> ) export { TodoLoad } //AppUI.js: ... {loading && ( <> <TodoLoad/> <TodoLoad/> <TodoLoad/> </> )}

      Error code:

      //TodoError.js import React from "react"; import './TodoError.css' const TodoError = () => { return ( <div className="TodoError"> <i className="fa fa-grav" aria-hidden="true"></i> <p>Vaya, parece que encontramos un problema, danos un poco de tiempo y vuelve a intenar en unos par de minutos...</p> </div> ) } export { TodoError } AppUI.js ... {error && ( <Modal> <TodoError /> </Modal> )}

      Empty code:

      //EmptyTodos.js import React from "react"; import './EmptyTodos.css' const EmptyTodos = () => { return ( <div className="EmptyTodos"> <div className="EmptyTodos-icons"> <i className="fa fa-sun-o" aria-hidden="true"></i> <i className="fa fa-smile-o" aria-hidden="true"></i> </div> <p>¡Crea tu primer TODO!</p> </div> ) } export { EmptyTodos } //AppUI.js ... {(!loading && !searchedTodos.length) && ( <EmptyTodos /> )}

      All completed state:

      //AllCompleted.js import React from 'react' import './AllCompleted.css' const AllCompleted = () => { return ( <div className="AllCompleted"> <i className="fa fa-coffee" aria-hidden="true"></i> <p>Has completado todos tus TODOs!</p> <p>Es hora de un merecido descanzo</p> </div> ) } export { AllCompleted } //AppUI.js ... {(allCompleted && !!searchedTodos.length) && ( <Modal> <AllCompleted /> </Modal> )}

      La variable AllCompleted es un estado que se actualiza al momento de cargar los todos en el custom hook de useLocalStorage, y también se actualiza cada vez que los todos sufren un cambio. Este es mi custom hook con las mejoras de la variable allCompleted y con el useEffect modificado para que solo se ejecute cuando se carga inicialmente

      //useLocalStorage.js import React from "react"; function useLocalStorage(itemName, initialValue) { const [error, setError] = React.useState(false) const [loading, setLoading] = React.useState(true) const [item, setItem] = React.useState(initialValue) const [allCompleted, setAllCompleted] = React.useState(false) const checkAllCompleted = (todos) => { if (Array.isArray(todos)) { const completedTodos = todos.filter(todo => !!todo.completed).length const totalTodos = todos.length if (totalTodos > 0 && (completedTodos === totalTodos)) { setAllCompleted(true) } else { setAllCompleted(false) } } } React.useEffect(() => { setTimeout(() => { try { const localStorageItem = localStorage.getItem(itemName) let parsedItems; if (!localStorageItem) { localStorage.setItem(itemName, JSON.stringify(initialValue)) parsedItems = initialValue } else { parsedItems = JSON.parse(localStorageItem) } setItem(parsedItems) checkAllCompleted(parsedItems) setLoading(false) } catch (error) { setError(error) } }, 1500); },[initialValue, itemName]) const saveItem = (newItem) => { try { localStorage.setItem(itemName, JSON.stringify(newItem)) setItem(newItem) checkAllCompleted(newItem) } catch (error) { setError(error) } } return { item, saveItem, loading, error, allCompleted } } export { useLocalStorage }

      Otras mejoras que hice fueron la implementación del hook useRef para que cuando la app cargue el foco esté en el input de búsqueda. Lo mismo cuando se carga el componente de TodoForm para que se ubique automáticamente en el textarea

      TodoSearch

      //TodoSearch/index.js ... const refInput = React.useRef() React.useEffect(() => { refInput.current.focus() },[]) return ( <section className="TodoSearch"> <input ref={refInput} value={searchValue} onChange={onSearchValueChange} placeholder="Cebolla" /> <i className="fa fa-search" aria-hidden="true"></i> </section> ) ...

      TodoForm

      En TodoForm también puse una validación para un máximo de 40 caracteres por Todo

      const textareaRef = React.useRef() React.useEffect(() => { textareaRef.current.focus() }, []) const onChange = (event) => { setNewTodoValue((prevState) => { let eventValue = '' if (event.target.value.length > 40) { eventValue = prevState } else { eventValue = event.target.value } return eventValue }) } const onCancel = () => { setOpenModal(false) } const onSubmit = (event) => { event.preventDefault() addTodo(newTodoValue) setOpenModal(false) } return ( <form className="TodoForm" onSubmit={onSubmit} > <label>Escribe tu nuevo <span>TODO</span></label> <textarea ref={textareaRef} value={newTodoValue} onChange={onChange} placeholder="Cortar la cebolla para el almuerzo" /> <p>{newTodoValue.length}/40</p> ... </form> ) ...

      Transparent Scroll

      En el TodoList puse una validación para que el alto no supere cierto máximo y así poder hacer scroll sin que el Botón se desplace demasiado y salga del viewport. Y ya que iba a aparecer un scroll le puse estilos para que luzca más estético:

      .TodoList { position: relative; max-height: 450px; margin: 15px 0; padding: 5px 5px; background: var(--background05); border-radius: 10px; overflow-y: scroll; } .TodoList-ul { display: flex; flex-direction: column; width: 280px; } /* * ============================== */ /* * ========== WEBKIT ============ */ /* * ============================== */ ::-webkit-scrollbar-thumb { background: rgba(255, 255, 255, 0.1); border-radius: 25px; } ::-webkit-scrollbar { width: 10px; background: rgba(255, 255, 255, 0.1); border-radius: 25px; } ::-webkit-scrollbar:hover { background: rgba(255, 255, 255, 0.2); box-shadow: 0 0 10px 10px rgba(0, 0, 0, 0.2); } ::-webkit-scrollbar:hover:active { background: rgba(255, 255, 255, 0.25); }

      Usé este video para entender como usar el useRef: https://egghead.io/lessons/react-focus-an-input-field-in-react-with-the-useref-and-useeffect-hooks

      En la clase de deploy con GitHub paso el link del repo ;-)

      loading.png

      yo solo lo puse una animación de carga

      Uy este esta muy interesante y bonito

      Que chimba!

      Hola devs !! Puede que haya un error en el Modal en el aspecto de sí al momento de agregar todos, nosotros no escribamos la tarea y le demos al botón de agregar , se agrega una tarea vacía, para evitar esto coloque el atributo required en textarea y así no se va una tarea vacía , haciendo que el usuario no ignore llenar este campo!!

      Podría ser contraproducente, ya que editando el html directamente desde el navegador el usuario podría crear y guardar un "ToDo" vacio y guardarlo en LocalStorage, lo mejor sería hacer las respectivas validaciones con operadores.

      Qué tal?

      localhost_3000__(iPhone 12 Pro) (5).png
      localhost_3000__(iPhone 12 Pro) (6).png

      10/10

      Muy buena la UI!

      Pantalla de carga

      La hice con la herramienta que nos dejaron en la lectura, bastante sencillo hacerlo con esa herramienta la verdad

      loading.png

      Usuario nuevo / o sin TO-DOs

      empty.png

      Por si hay un error ya saben donde ir

      error.png

      Vamos avanzando, está muy bueno el proyecto para llevar varios conceptos

      capturas.png

      Aporte en acción:

      . .

      Pantalla de empty todos

      . .

      Pantalla de error todos

      . .

      Pantalla de loading skeletons

      Loading State:

      EmptyToDos:

      TheProjectWithTodos:

      Que elegante! Usaste solo css?

      Usando React Content Loader, aunque debo decir que no me siento cómodo usando medidas absolutas. En un futuro veré cómo hacerlo con medidas relativas.

      import React from 'react' import ContentLoader from 'react-content-loader' export function TodosLoading(props) { return ( <ContentLoader speed={2} width={272} height={102} viewBox="0 0 272 104" backgroundColor="#d9d9d9" foregroundColor="#ecebeb" {...props} > {/* Línea Izquierda */} <rect x="0" y="16" rx="0" ry="0" width="2" height="102" /> {/* Línea derecha */} <rect x="270" y="16" rx="0" ry="0" width="2" height="102" /> {/* Línea superior */} <rect x="0" y="16" rx="0" ry="0" width="272" height="2" /> {/* Línea inferior */} <rect x="0" y="102" rx="0" ry="0" width="272" height="2" /> {/* Check */} <rect x="24" y="48" rx="0" ry="0" width="24" height="24" /> {/* Texto */} <rect x="72" y="28" rx="0" ry="0" width="176" height="24" /> {/* Texto */} <rect x="72" y="68" rx="0" ry="0" width="176" height="24" /> </ContentLoader> ) }
      skeleton.png

      Seria bueno usar los React-Icons?

      Yeah, está perfecto :)

      Aquí mi propuesta de LOADING...

      Screenshot_20230216_083352.png

      Tiene una pequeña animación que no supe como cargarla

      localhost_3000_(Surface Duo) (1).png
      localhost_3000_.png
      localhost_3000_(Surface Duo).png
      localhost_3000_(Surface Duo) (3).png
      localhost_3000_(Surface Duo) (2).png

      decidí crear algo mas expresivo EmptyTodos

      starTodo.png

      TodosErrot

      ErrorTodos.png

      TodoLoading

      LoadingTodos.png

      Love it

      Mi Todo List

      Task3.png
      Captura de Pantalla 2022-01-17 a la(s) 13.06.40.png

      La verdad no hice muchos cambios al css :C

      introreact.JPG
      introreact2.JPG