Platzi
Platzi

LA EDUCACI脫N ES UN REGALO 隆ACCEDE AL PRECIO ESPECIAL!

Antes: $249
$149
Currency
Antes: $249
Ahorras: $100
COMIENZA AHORA
Termina en: 16D : 15H : 47M : 47S

隆Bienvenida! Este es un curso especial de React Hooks

1

驴Qu茅 aprender谩s en el Curso Profesional de React Hooks?

2

驴Qu茅 son los React Hooks y c贸mo cambian el desarrollo con React?

Introducci贸n a React Hooks

3

useState: estado en componentes creados como funciones

4

useEffect: olvida el ciclo de vida, ahora piensa en efectos

5

useContext: la fusi贸n de React Hooks y React Context

6

useReducer: como useState, pero m谩s escalable

7

驴Qu茅 es memoization? T茅cnicas de optimizaci贸n en programaci贸n funcional

8

useMemo: evita c谩lculos innecesarios en componentes

9

useRef: manejo profesional de inputs y formularios

10

useCallback: evita c谩lculos innecesarios en funciones

11

Optimizaci贸n de componentes en React con React.memo

12

Custom hooks: abstracci贸n en la l贸gica de tus componentes

13

Third Party Custom Hooks de Redux y React Router

Configura un entorno de desarrollo profesional

14

Proyecto: an谩lisis y retos de Platzi Conf Store

15

Instalaci贸n de Webpack y Babel: presets, plugins y loaders

16

Configuraci贸n de Webpack 5 y webpack-dev-server

17

Configuraci贸n de Webpack 5 con loaders y estilos

18

Loaders de Webpack para Preprocesadores CSS

19

Flujo de desarrollo seguro y consistente con ESLint y Prettier

20

Git Hooks con Husky

Estructura y creaci贸n de componentes para Platzi Conf Store

21

Arquitectura de vistas y componentes con React Router DOM

22

Maquetaci贸n y estilos del home

23

Maquetaci贸n y estilos de la lista de productos

24

Maquetaci贸n y estilos del formulario de checkout

25

Maquetaci贸n y estilos de la informaci贸n del usuario

26

Maquetaci贸n y estilos del flujo de pago

27

Integraci贸n de 铆conos y conexi贸n con React Router

Integraci贸n de React Hooks en Platzi Conf Merch

28

Creando nuestro primer custom hook

29

Implementando useContext en Platzi Conf Merch

30

useContext en la p谩gina de checkout

31

useRef en la p谩gina de checkout

32

Integrando third party custom hooks en Platzi Conf Merch

Configura mapas y pagos con PayPal y Google Maps

33

Paso a paso para conectar tu aplicaci贸n con la API de PayPal

34

Integraci贸n de pagos con la API de PayPal

35

Completando la integraci贸n de pagos con la API de PayPal

36

Paso a paso para conectar tu aplicaci贸n con la API de Google Maps

37

Integraci贸n de Google Maps en el mapa de checkout

38

Creando un Custom Hook para Google Maps

Estrategias de deployment profesional

39

Continuous integration y continuous delivery con GitHub Actions

40

Compra del dominio y despliega con Cloudflare

Optimizaci贸n de aplicaciones web con React

41

Integraci贸n de React Helmet para mejorar el SEO con meta etiquetas

42

An谩lisis de performance con Google Lighthouse

43

Convierte tu aplicaci贸n de React en PWA

Bonus: trabaja con Strapi CMS para crear tu propia API

44

Crea una API con Strapi CMS y cons煤mela con React.js

驴Qu茅 sigue en tu carrera profesional?

45

Pr贸ximos pasos para especializarte en frontend

Crea una cuenta o inicia sesi贸n

隆Contin煤a aprendiendo sin ning煤n costo! 脷nete y comienza a potenciar tu carrera

useEffect: olvida el ciclo de vida, ahora piensa en efectos

4/45
Recursos

Aportes 107

Preguntas 11

Ordenar por:

Los aportes, preguntas y respuestas son vitales para aprender en comunidad. Reg铆strate o inicia sesi贸n para participar.

Cada d铆a tengo mayor fluidez con React 馃槏

隆Hola!
Aqu铆 comparto mi soluci贸n al reto:

Escrib铆 un art铆culo en mi blog que quiz谩s les ayude a complementar los conocimientos que Oscar transmiti贸 en esta clase 鉂も潳
.
Link del articulo: https://dartiles.dev/blog/useeffect-react-hooks-ciclos-de-vida
.

.
Este blog es un claro reflejo de los conocimientos que adquir铆 en el curso practico de sapper y svelte de Oscar

Comparto el useEffect usando asyn/await

import React, { useState, useEffect } from "react";

const Characters = () => {
  const [characters, setCharacters] = useState([]);

  const getCharacters = async () => {
    const response = await fetch("https://rickandmortyapi.com/api/character");
    const data = await response.json();
    setCharacters(data.results);
  };

  useEffect(() => {
    getCharacters();
  }, []);

  return (
    <div className="Characters">
      {characters.map((character) => (
        <h2 key={character.id}>{character.name}</h2>
      ))}
    </div>
  );
};

export default Characters;

useEffect: olvida el ciclo de vida, ahora piensa en efectos


useEffect nos permite manejar efectos que van a ser transmitidos dentro del componente.
En este ejemplo se llama a una API, traemos la informaci贸n y la ejecutaremos en el componente

  1. Creamos el componente Characters.jsx
  2. Usamos el API de RickandMorty


Characters.jsx

// importar useState y useEffect
import React, {useState, useEffect} from 'react'


const Characters = () => {
    /**
     * L贸gica de useState
     * constante donde internamente estructuramos los elementos que necesitamos
     * de useState y lo iniciamos como un vector vac铆o
     */
    const [characters, setCharacters] = useState([]);
    
    /**
     * L贸gica de useEffect
     * es una funci贸n con 2 par谩metros
     * el primero es una funci贸n an贸nima donde va a estar la l贸gica
     * el segundo es una variable que esta escuchando si hay cambios 
     */
    useEffect(() => {
        // useEffect llama a fetch, el cual obtiene la informacion de la api de RickAndMorty
        fetch('https://rickandmortyapi.com/api/character/')
        .then(response => response.json())
        .then(data => setCharacters(data.results));
    }, [])
    
    /** 
     * Nombre del personaje
     * Iteramos por cada uno de los elementos
     */
    return (
        <div className="Characters">
            {characters.map(character => (
                <h2>{character.name}</h2>
            ))}
        </div>
    )
}

export default Characters


App.js

import React from 'react'
// Importamos componente Header
import Header from './components/Header';
// Importamos componente Characters
import Characters from './components/Characters';
import './App.css';

function App() {
  return (
    <div className="App">
      <Header />
      <Characters />
      <h1>
        Hola Mundo
      </h1>
    </div>
  );
}

export default App;

Como el darkmode es una variable global, sub铆 la logica al nivel de App.js y desde header solo retorno la funci贸n que hace el cambio de color.

Usando bootstrap con sus clases para definir colores de texto y fondo, rapidamente puedes hacer este efecto de darkmode y responsive.

import "./App.css";
import React, { useState } from "react";

import { Header } from "./components/Header";
import Characters from "./components/Characters";

function App() {
  const [darkMode, setDarkMode] = useState(false);
  let bg = darkMode ? "bg-dark text-light" : "bg-light text-dark"
  return (
    <>
        <div className= {"App "+ bg}>
          <Header
            darkMode={darkMode}
            onClick={() => setDarkMode(!darkMode)}
          ></Header>
          <Characters></Characters>
        </div>
      
    </>
  );
}

export default App;
import React from 'react'

export const Header =( props )=>{
    
    return(
        <div className="Header ">
            <h1>React Hooks</h1>
            <button type="button" onClick={()=>props.onClick()}>
                {props.darkMode ? "Light Mode" : "Dark Mode"}
            </button>
        </div>

    )
}


import React, { useEffect, useState } from "react";

const Characters = () => {
  const [characters, setCharacters] = useState([]);

  useEffect(() => {
    fetch("https://rickandmortyapi.com/api/character/")
      .then((r) => r.json())
      .then((data) => setCharacters(data.results));
  }, []);

  return (
    <div className="container">
            
        <div className="row">
      {characters.map((character) => (
        <div className="col">
          <>
          <img className="character__img" src={character.image} alt="" />
          <h2 className="character__name">{character.name}</h2>
          </>
          </div>
          ))}
    </div>
    </div>
  );
};

export default Characters;

Challenge done

useEffect

Nos ayuda con todo lo que tiene que ver con efectos secundarios. Algunos ejemplos de efectos secundarios son:

-Peticiones de datos

-Establecimiento de suscriptores

-Actualizaciones manuales del DOM

**Caracter铆sticas de useEffect **

-Recibe dos par谩metros: el primero una funci贸n y el segundo un array cuyos valoren ser谩n variables de las que depende nuestro efecto(este array es opcional).

-Se va a ejecutar en cada renderizado del componente incluyendo el primero.

-Puedes usar m谩s de un useEffect x componente

-Est谩 dise帽ado para que la funci贸n que pases como par谩metro retorna a su vez otra funci贸n, React va a ejecutar dicha funci贸n cuando se desmonte el componente

(BONUS) Como funciona el ciclo de vida de un componente?

OJO el ciclo de vida de un componente es un concepto para trabajar con componente de tipo clases, pero se me hac铆a interesante entender como funcionaba.

Veremos solo como funciona por encima cada ciclo tiene sus propios componentes para poder manejarlo.

El ciclo de vida de React se puede dividir en 3 fases, que son: 脡l montando, la actualizaci贸n y el desmontado

Montado: Esta es la primera fase y ocurre una vez por componente cuando este se crea y se monta en la UI.

Actualizaci贸n: Esta puede ocurrir ninguna o m煤ltiples veces, esta fase sucede cuando ocurre alg煤n cambio en el componente y por lo tanto requiere que la UI se vuelva a generar para que se puedan percibir estos cambios.

Desmontado: Es la 煤ltima fase y se puede ejecutar un m茅todo antes de que se desmonte el componente de la UI.

驴Cu谩l es la diferencia entre el useEffect y los metodos del ciclo de vida?

La diferencia esta en que cuando usamos useEffect no pensamos en el cuando(en que fase estamos) si no, en el por qu茅 el efecto se va a ejecutar(cambios en una dependencia).

Para profundizar les comparto lo siguiente blog: https://matiashernandez.dev/react-useeffect-hook-comparado-con-los-estados-del-ciclo-de-vida

https://platzi.com/blog/ciclo-de-vida-de-un-componente-de-reactjs/#:~:text=El ciclo de vida se,o despu茅s de cierta acci贸n.

Usando tailwindcss

import React, { useState, useEffect } from 'react';

const Characters = () => {
  const [characters, setCharacters] = useState([]);
  const API = 'https://rickandmortyapi.com/api/character/';
  useEffect(() => {
    fetch(API)
      .then(response => response.json())
      .then(data => setCharacters(data.results));
  }, []);

  return (

    <section className="text-gray-700 body-font ">

      <div className="flex flex-col text-center w-full mb-20">
        <h1 className="sm:text-3xl text-2xl font-medium title-font mb-4 text-gray-900">Rick and Morty</h1>
        <p className="lg:w-2/3 mx-auto leading-relaxed text-base">All Characters</p>
      </div>
      <div className="container px-5 mx-auto ">
        <div className=" flex flex-wrap -m-2">
          {characters.length > 0 && characters.map(character => (
            <div className="p-2 lg:w-1/3 md:w-1/2 w-full">
              <div className="h-full flex items-center border-gray-200 border p-4 rounded-lg">
                <img alt="team" className="w-16 h-16 bg-gray-100 object-cover object-center flex-shrink-0 rounded-full mr-4" src={character.image} />

                <div className="flex-grow">
                  <h2 className="text-gray-900 title-font font-medium">{character.name}</h2>
                  <p className="text-gray-500">{character.status}</p>
                  <p className="text-gray-500">{character.species}</p>

                </div>
              </div>
            </div>
          ))}
        </div>

      </div>
    </section>
  );
};

export default Characters;

Y as铆 quedo. 馃槂

Para realizar el cambio de estilo hice un levantamiento de estado, es decir pase el estado de darkMode en Header.jsx hacia App.js. Me quedo algo asi:

Header.jsx

import React from 'react';

import 'bootstrap/dist/css/bootstrap.css';

const Header = (props) => {
  // const [darkMode, setDarkMode] = useState(false);
  // const appClass = document.getElementById('header');
  // console.log(appClass);
  
  return (
    <div id="header" className="Header">
      <h1>ReackHooks</h1>
      <button 
      className="btn btn-dark"
      onClick={props.onHandleClick} 
      type="button"
      >
          {
          props.darkMode?
          'DarkMode':
          'LightMode'
          }
      </button>
    </div>
  )}
          
export default Header;

En app.js

import React, { useState } from "react";

import Character from "./components/Character";
import Header from "./components/Header";

import "./App.css";

function App() {
  const [darkMode, setDarkMode] = useState(false);

  const handleClick = () => {
    setDarkMode(!darkMode);
    const candleButton = document.querySelector(".App");
    const title = document.querySelector(".App__title");

    candleButton.classList.toggle("dark-background");
    title.classList.toggle("dark-title");
  };
  return (
    <div className="App">
      <h1 className="App__title">Rick and Morty Characters</h1>
      <Header onHandleClick={handleClick} darkMode={darkMode} />
      <Character />
    </div>
  );
}

export default App;

En el navegador鈥
.
LightMode

.
.
DarkMode

En este articulo Dan Abramov explica el useEffect a profundidad
https://overreacted.io/a-complete-guide-to-useeffect/

Creo que falto explicar la funcion del return dentro de un -useEffect-

useEffect(() => {
  // aqui se ejecuta las funciones del efecto;
  return () => {
    //aqui se ejecuta el Desmontaje del Efecto;
	// esto es lo que me refiero
  }
}, [props.ActualizaEstado])

Les dejo este Link, en el minuto 19:40 empiezan a explicar la funcion del return
https://www.youtube.com/watch?v=kPx2qgDS3ak&list=PLvq-jIkSeTUZ5XcUw8fJPTBKEHEKPMTKk&index=17&t=594s

Les comparto mi soluci贸n para el estilizado utilize Semantic UI recomiendo mucho ese Framework

Una peculiaridad de useEffect es que la funci贸n que le pasamos (effect) se ejecuta despues del primer render y despues de cada update. Es como si cada efecto perteneciera a un render en particular

Comparto mis resultados hasta el momento:

Comparto mi versi贸n del ejercicio

Empezando el curso tome la decision de revisar la documentacion de hooks de react mientras pasaba por cada uno, y realmente fue una decision excelente, les recomiendo leer la doc mientras pasan por cada hook, ademas de aprender como funciona completamente el hook tambien te ofrecen buenas practicas al momento de utilizar lo hooks

De essta manera se puede construir el efecto utilizando async/await. que vuelve el codigo y el manejo de la asincronia muhco mas legible

import { useEffect, useState } from "react";

const Character = (props) => {
  const [characters, setCharacters] = useState([]);

  useEffect(() => {
    const fetchCharacters = async () => {
      const response = await fetch("https://rickandmortyapi.com/api/character");
      const data = await response.json();
      setCharacters(data.results);
    };

    fetchCharacters();
  }, []);

  return (
    <div className="charachters">
      {characters.map((character) => {
        return <h2> {character.name} </h2>;
      })}
    </div>
  );
};

export default Character;

Les comparto como qued贸 mi dise帽o

Este es el c贸digo para implementar el Dark Mode

useEffect hace disponibles los m茅todos del ciclo de vida componentDidMount, componentDidUpdate y componentWillUnmount combinados en componentes funcionales. Tener presente que si no se pasa un array como segundo argumento:

useEffect(() => {
	// aqu铆 va el c贸digo
});

el c贸digo dentro de useEffect se ejecutar谩 cada vez que el componente se re-renderiza (la primera vez que es renderizado y en cada actualizaci贸n). Similar a componentDidMount y componentDidUpdate.

Pasando un array vac铆o como segundo argumento:

useEffect(() => {
	// aqu铆 va el c贸digo
},  []);

el c贸digo se ejecuta una vez cuando el componente es montado (componentDidMount).

Pasando un array no vac铆o como segundo argumento:

useEffect(() => {
	// aqu铆 va el c贸digo
},  [value])

el c贸digo se ejecutar谩 cada vez que el valor (value) cambia. Tambi茅n puedes pasar m煤tiples valores y React renderiza el componente si cualquiera de los elementos del arreglo cambi贸.

useEffect puede retornar una funci贸n el cual se ejecuta cuando el componente se desmonta (componentWillUnmount).

useEffect(() => {
	// c贸digo
	return () => {
		// c贸digo
	}
});

Usando el hook de Efecto

Estas clases me han servido para llevar a cabo unas animaciones en mi portfolio 馃挭

Ahora queda perfeccionarlas y a帽adirle el Dark Mode 馃憖

Si quieres ver c贸mo lo hice, te dejo por aqu铆 el enlace al repo 馃槃
He de decir que, adem谩s de useState y useEffect, tuve que saber algunas cosas acerca del react-router-dom, pero no es muy complicado si lo quieres hacer 馃槈

Les recomiendo esta lectura de la documentaci贸n oficial de React Hooks en espa帽ol sobre el uso de el hook useEffect . Pens茅 en resumirla pero es mucho mejor darle una lectura completa para esclarecer cualquier duda.
https://es.reactjs.org/docs/hooks-effect.html

Cumpliendo el reto

Asi me fue鈥

Apuntes

  • Dejar de usar el ciclo de vida y pensar en efectos
  • Nos permite manejar efectos los cuales ser谩n transmitidos dentro de nuestros componentes
    • Trabajando l贸gica interna seg煤n sea el caso

conceptos claves
useEffect.- El Hook de efecto te permite llevar a cabo efectos secundarios en componentes funcionales:

Aqui mi reto realizado:
LightMode

DarkMode

Muy bueno, por lo general lo que pon铆a dentro del effect y que actualizaba el state se quedaba en un ciclo infinito de renderizado. ahora veo que es por ese segundo parametro de dicho hooks en donde se pone las variables que escuchar谩n. Y que si se deja vaci贸 solo hace el render una vez.

Pude hacer lo del renderizado, pero no lo de darkmode, ya que eso estaba en el Header y no s茅 como pasar props de hijo a padre (App.js) para luego pasarselo a (Characters.jsx).
驴Alguno sabe?

As铆 qued贸:

Cree un componente 鈥淐harater鈥, me parece que se ve mejor as铆:
characters.jsx:

import React, { useState, useEffect } from "react";
import Character from './Character'
import './style/characters.css'

const Characters = () => {
  const [characters, setCharacters] = useState([]);

  useEffect(() => {
    fetch("https://rickandmortyapi.com/api/character/")
      .then((res) => res.json())
      .then((data) => setCharacters(data.results));
  }, []);

  return (
    <div className="characters">
      {characters.map((character) => {
       return <Character
                name={character.name}
                status={character.status}
                img={character.image}
                key={character.id}
                />;
      })}
    </div>
  );
};

export default Characters;

Character.jsx:

import React from 'react'
import './style/character.css'

const Character = ({name,status,img}) => {

    return (
        <div className="character_container">
            <img src={img} alt=""/>
            <div className="character_data">
                <h2>{name}</h2>
                <h2>{status}</h2>
            </div>
        </div>
    )
}

export default Character

Aun estoy pensando en los colores del dark theme 馃榿

Anduve haciendolo aplicando mobile first:
Mobile:

Tablet:

Desktop:

Modo oscuro:

Algo muy importante a destacar en useEffect es sus segundo argumento. Un arreglo de dependencias que controlan cuando deber铆a ser ejecutado.

Las dependencias son valores definidos fuera del Hook pero son utilizados dentro.

Cabe resaltar que el segundo argumento es opcional y depende su uso React ejecutara el useEffect.

  1. Si no se le pasa un arreglo. React ejecutara el useEffect en cada render.
  2. Un arreglo vacio: React ejecutara el useEffect solo una vez
  3. Un arreglo con dependencias: React compara e valor actual de la dependencia con el valor que tenia antes de hacer el render. Si el valor no coincide entonces el useEffect vuelve a ejecutarse.

Recomiendo las siguentes lecturas sobre diferencias con el componentDidMount y el infinite-loop

para quitar los Warning de la consola debes agregarle un return al useEffect

 useEffect(() => {
    fetch('https://rickandmortyapi.com/api/character/')
    .then(response => response.json())
    .then(data => setCharacteres(data.results))
    return
  }, [])

y como hicimos un map agregarle el key

 return (
    <div className="characteres" key="index">
      {characters.map(character => (
        <h2>{character.name}</h2>
      ))}
    </div>
  );

Me gustar铆a mucho que explicaran por que es buena practica usar return en el useEffect

Yo empece a implementar los stylesd-components, una manera pr谩ctica y rapida de dar estilos en React
![](

![](

asi me quedo el codigo

App.js

import React from 'react';
import Header from './components/Header';
import Characters from './components/Characters';
import useDarkMode from './Hooks/useDarkMode';

import './App.css';

function App() {
  const [darkMode, setDarkMode] = useDarkMode();
  return (
    <div className="App"  style={darkMode? {backgroundColor: 'black', color: 'white'} : {backgroundColor: 'white'}}>
      <Header darkMode={darkMode} setDarkMode={setDarkMode}/>
      <Characters darkMode={darkMode} />
    </div>
  );
}

export default App;

Cree un Hook para manejar el Darkmode

import React, { useState } from 'react';

function useDarkMode() {
  const [darkMode, setDarkMode] = useState(false);

  return [darkMode, setDarkMode];
}

export default useDarkMode;

Characters.jsx

import React, {useState, useEffect} from 'react';
import styled from 'styled-components';

const Container = styled.div`
  background-color: ${props => props.darkMode ? 'black' : 'white'};
  color: ${props => props.darkMode ? 'white' : 'black'};
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(240px, 1fr));
  gap: 20px;
  margin: 10px;

`
const Card = styled.div`
  height: 100%;
  box-shadow: 2px 6px 8px rgba(0,0,0,0.19);
  border-radius: 8px;

  img {
    width: 100%;
    border-radius: 8px;
  }

  h2,h4 {
    margin: 0;
    margin-bottom: 4px;
  }
`

const Characters = ({darkMode}) => {
  const [characters, setCharacters] = useState([]);

  useEffect(() => {
    fetch('https://rickandmortyapi.com/api/character/')
      .then(response => response.json())
      .then(data => setCharacters(data.results))
  }, []);

  return (
    <Container darkMode={darkMode}>
    {
    characters.map(character => (
      <Card >
        <img src={character.image} alt={character.name} />
        <div>
          <h2>{character.name}</h2>
          <h4>{character.status}</h4>
          <h4>{character.species}</h4>
          <h4>{character.gender}</h4>
        </div>
      </Card>
    ))
    }
    </Container>
  )
}

export default Characters;

Header.jsx

import React, { useState } from 'react';
import '../styles/Header.css';


const Header = ({darkMode, setDarkMode}) => {
  const handleClick = () => {
    setDarkMode(!darkMode);
  }

  return (
    <div className="Header" style={darkMode? {backgroundColor: 'black', color: 'white'} : {backgroundColor: 'white'}}>
      <h1>ReactHooks</h1>
      <button
        style={darkMode? {color: 'white'} : null}
        type="button"
        onClick={handleClick}>
        {darkMode? 'Dark Mode' : 'Light Mode'}
      </button>
    </div>
  )
}

export default Header;

algo de estilo en styles/Header.css

.Header {
  display: flex;
  justify-content: space-evenly;
}
.Header button {
  background-color: transparent;
  margin: auto 0;
  height: 40px;
  width: 120px;
  font-size: 15px;
  font-weight: bold;
}

React.UseEffect
Ejecuta el c贸digo que tengamos adentro justo antes de que React tiene todo listo para renderizar el componente

  • El segundo argumento que le enviamos al hook puede ser un array vac铆o [ ], este le dice al UseEffect que solo se ejecute la primera vez que el componente se renderize, cuando se rerenderize no se ejecutara. Si dejamos ese segundo argumento vac铆o, el hook se ejecutara todas las veces que nuestro componente se rerenderize.
  • Podemos ejecutarlo cuando hayan cambios espec铆ficos en cierta variable o cierto componente, para eso podemos poner como segundo argumento un arreglo y adentro el nombre de la variable: [TotalToDos].

Creo que es recomendable no escribir la logica de llamado a la api dentro del useeffect, habria que llamar a una funci贸n que se encargue de eso.

Disfrutando y aprendiendo hooks! Me encanta que despues de las lecciones hay mini challenges!

import React, { useState, useEffect } from 'react'

const Characters = () => {
    const [characters, setCharacters] = useState([]);

    useEffect(() => {
        fetch("https://rickandmortyapi.com/api/character/")
        .then(response => response.json())
        .then(data => setCharacters(data.results))

    },[]);
 

    return (
        <div className="container mt-5">
            <div className="row">
            {characters.map(character => (
                <>
                <div className="col-4 mb-5">
                <div className="border border-dark p-3">
                    <img src={ character.image } alt="" />
                    <h4>{ character.name }</h4>
                    <h6>{ character.status } - { character.species }<small> ({ character.gender }) </small></h6>
                </div>
                </div>
                </>
            ))}
            </div>
        </div>
    )
}

export default Characters

En App.js

import React, { useState } from 'react';

import Header from './components/Header';
import Characters from './components/Characters';
import './App.css';

function App() {
  const [darkMode, setDarkMode] = useState(false);

  const handleClick = () => {
    setDarkMode(!darkMode);
  }

  return (
    <div className={`App ${darkMode ? 'dark-mode' : 'light-mode'}`}>
      <Header handleClick={handleClick} darkMode={darkMode} />
      <Characters />
    </div>
  );
}

export default App;

Header.jsx

import '../assets/styles/components/Header.css';

const Header = props => {
    const { handleClick, darkMode } = props
    return (
        <div className={`header`}>
            <h1>ReactHooks</h1>
            <button className={`button-mode ${darkMode ? 'light-mode' : 'dark-mode'}`} type="button" onClick={handleClick}>{darkMode ? 'Light mode' : 'Dark mode'}</button>
        </div>
    );
}

export default Header;

Dejo por ac谩 mi reto:


Reto cumplido

useEffect

馃挕 INFO
Funci贸n para realizar efectos secundarios.
Se puede pensar como un s铆mil de los m茅todos del ciclo de vida componentDidMount, componentDidUpdate y componentWillUnmount combinados, pero sin bloquear el proceso de actualizaci贸n de la vista.

.
Se puede considerar como efectos secundarios como:

  • Obtenci贸n de datos
  • Configuraciones
  • Cambios manuales/predefinidos del DOM
    .

En conjunto, hay 2 tipos de efectos secundarios: los que requieren y no de limpieza.
.

馃挕INFO
Se dice limpieza/barrido al recurso que necesita, por l贸gica, encadenar un proceso ya sea por actualizaci贸n o desmontaje.

.

Efectos sin limpieza

Ejemplos como:

  • Peticiones de red
  • Mutaciones manuales del DOM
    .
import React, { useState, useEffect } from 'react';

function Example() {
  const [count, setCount] = useState(0);

  useEffect(() => {
    document.title = `You clicked ${count} times`;
  });

  return (
    <div>
      <p>You clicked {count} times</p>
      <button onClick={() => setCount(count + 1)}>
        Click me
      </button>
    </div>
  );
}
  • Al usar de esta forma useEffect , le comunicamos a React que el componente necesita realizar algo despu茅s de un renderizado.
  • La funci贸n se ejecuta en cada primer renderizado y despu茅s de cada actualizaci贸n.

.

馃挕INFO
useEffect se ejecuta en cada primer renderizado y despu茅s de cada actualizaci贸n.

.

Efectos con limpieza

Ejemplos como:

  • Registro de sesi贸n/subscripci贸n
import React, { useState, useEffect } from 'react';

function FriendStatus(props) {
  const [isOnline, setIsOnline] = useState(null);

  **useEffect**(() => {
    function handleStatusChange(status) {
      setIsOnline(status.isOnline);
    }
    ChatAPI.**subscribeToFriendStatus**(props.friend.id, handleStatusChange);

    return function **cleanup**() {
      ChatAPI.**unsubscribeFromFriendStatus**(props.friend.id, handleStatusChange);
    };
  });

  if (isOnline === null) {
    return 'Loading...';
  }
  return isOnline ? 'Online' : 'Offline';
}
  • Dentro de useEffect , cuando se retorna una funci贸n, indicamos el modo en que se limpia/barre.
  • La funci贸n cleanup es de fines pr谩cticos, se debe utilizar arrow functions
return () => {
	ChatAPI.unsubscribeFromFriendStatus(props.friend.id, handleStatusChange);
    };

馃挕 INFO
React lanza una limpieza cuando el componente se desmonta o como acci贸n previa a cada siguiente efecto.

.
El API de useEffect permite un par谩metro adicional indicando 2 efectos:

  • [] - Si deseamos ejecutar un efecto y limpiarlo una vez, desvinculando la funci贸n de los cambios de los props o state , por consiguiente estos 煤ltimos dentro de la funci贸n siempre tendr谩n sus valores iniciales.
  • [vale] - Se omite el flujo natural de la funci贸n, ejecut谩ndose solamente cuando el valor ha cambiado.

.

Ejemplo Characters.jsx

import React, { useState, useEffect } from "react";

const Characters = () => {
  const [characters, setCharacters] = useState([]);
  useEffect(() => {
    async function fetchCharacters() {
      const response = await fetch("https://rickandmortyapi.com/api/character");
      const data = await response.json();
      setCharacters(data.results);
    }
    fetchCharacters();
  }, []);

  return (
    <div className="characters">
      {characters.length > 0 &&
        characters.map(({ id, name }) => <h2 key={id}>{name}</h2>)}
    </div>
  );
};

export default Characters;

鈿狅笍 NOTA
Pese que CRA o Create React App incorpora React din谩mico para no implementarlo, se sugiere definirlo por estabilidad a nivel componente

Hola les comparto mi solici贸n

El codigo seria este

import './App.css';
import Characters from './components/Characters';
import Header from './components/Header';

function App() {
  return (
    <div className="App">
      <Header></Header>
      <hr></hr>
      <Characters></Characters>
    </div>
  );
}

export default App;
import React, { useEffect, useState } from 'react'
import hook from '../assets/hook.svg'
import titulo from '../assets/titulo.png'

const Header = () => {

   const [darkmode, setDarkmode] = useState(false)

   const handleDarkMode = () => {

      setDarkmode(!darkmode)

   }

   useEffect(() => {

      document.documentElement.style.setProperty("--primary", `${darkmode ? '#fff ' : 'rgb(32, 35, 41)'}`)
      document.documentElement.style.setProperty("--fondo", `${darkmode ? 'rgb(32, 35, 41)' : '#fff'}`)

   }, [darkmode, setDarkmode])

   return (
      <div className="Header">
         <div className="Header__titulo">
            ReactHook <img src={hook} alt="hook" />
         </div>

         <div className="Header__rickmorty">
            <img src={titulo} alt="" />
         </div>

         <div className="Header__darkmode">
            <button
               onClick={handleDarkMode}
               style={{
                  backgroundColor: darkmode ? '#477385' : '#fff',
                  color: darkmode ? '#fff' : '#477385',
               }}
            >
               {(darkmode) ?
                  (<i className="fa fa-moon-o" aria-hidden="true"></i>) :
                  (<i className="fa fa-sun-o" aria-hidden="true"></i>)}

            </button>
         </div>
      </div>
   )
}

export default Header

import React, { useEffect, useState } from 'react'

const Characters = () => {


   const [characters, setCharacters] = useState([])

   useEffect(() => {

      fetch("https://rickandmortyapi.com/api/character")
         .then(response => response.json())
         .then(data => {
            console.log(data);
            setCharacters(data.results)
         })

   }, [])

   const statusCharacter = (status) => {
      switch (status) {
         case 'Dead':
            return 'muerto'

         case 'unknown':
            return 'desconocido'

         default:
            return 'vivo'
      }
   }

   return (
      <div className="Personajes">
         {characters.map(character => (
            <div className="Personajes__item animate__animated animate__fadeIn" key={character.name}>
               <div className="Personajes__imagen">
                  <img src={character.image} alt={character.name} />
               </div>
               <div className="Personajes__info">
                  <p className="nombre"> <strong>{character.name}</strong></p>
                  <p className="nombre"> <strong>{character.species}</strong></p>
                  <p className="nombre"> <strong>{character.gender}</strong></p>
                  <p className="nombre"> <strong>{character.status}</strong> <span className={`estado ${statusCharacter(character.status)}`}></span> </p>
               </div>
            </div>
         ))}
      </div>
   )
}

export default Characters

El css:

index.js

:root{
  --fondo:#fff;
  --primary:rgb(32, 35, 41);
}
*{
  margin: 0;
  padding: 0;
  box-sizing: border-box;
}

body {
  background-color: var(--fondo);
  height: 100vh;
  margin: 0;
  font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
    'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
    sans-serif;
    /* font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol"; */
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
}

App.css

.App {
  text-align: center;
  padding: 15px 20px 0 20px;
}

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

.Header{
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding-bottom: 15px;
}

.Header__titulo{
  font-weight: bold;
  display: flex;
  align-items: center;
  font-size: 20px;
  color: var(--primary);
 
}

.Header__titulo img{
  width: 30px;
  margin-left: 0px;
}

.Header__rickmorty img{
  width: 250px;
}

.Header__darkmode button{
  border: none;
  outline: none;
  width: 50px;
  height: 50px;
  border-radius: 50%;
  cursor: pointer;
  font-weight: 900;
  font-size: 25px;
  color: #477385;
  background-color: #fff;
  border: 2px solid #477385;
  transition: all .3s;
}

.Personajes{
  padding: 30px 0;
  display: flex;
  flex-wrap: wrap;
  gap: 20px;
  justify-content: center;
}

.Personajes__item{
  width: 240px;
  box-shadow: 0 4px 8px 0 rgba(0,0,0,0.2);
  border-radius: 7px;
  overflow: hidden;
  background-color: #fff;
}

.Personajes__info{
  padding: 20px;
  
}

.Personajes__item img{
  width: 100%;
  display: flex;
}

.Personajes__info p{
  color: rgb(158, 158, 158);
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 5px;
}

.Personajes__info p strong{
  color: rgb(32, 35, 41);
}

.Personajes__info .estado{
  width: 6px;
  height: 6px;
  
  border-radius: 50%;
  line-height: 0;
}

.Personajes__info .estado.vivo{
  background-color: rgb(85, 204, 68);
}

.Personajes__info .estado.muerto{
  background-color: rgb(214, 61, 46);
}

.Personajes__info .estado.desconocido{
  background-color: rgb(158, 158, 158);
}

Me falt贸 el dark/light mode, pero ma帽ana continuo xD

Usando asincron铆a a la llamada de la API:

const apiURL = 'https://rickandmortyapi.com/api/character/';

  useEffect(() => {
    async function fetchCharacters() {
      const response = await fetch(apiURL);
      const characters = await response.json();
      setCharacters(characters.results);
    }
    fetchCharacters();
  }, []);

useEffect se puede usar para peticiones, funciones que funcionen asyncronamente, llamados a API !! 馃槃

Recordemos que useEffect empieza a trabajar una vez el componente empiece a ser renderizado. Cada vez que se llame el componente se estara haciendo paralelamente la peticion a la api. o lo que sea que hagamos dentro del useEffect

Mi reto cumplido馃槑馃槑

Hola mi reto鈥

Aporte: Una forma m谩s f谩cil de importar los componentes es escribiendo el nombre del componente y presionar tab (o buscar la ruta en las opciones que se despliegan), esto har谩 la importaci贸n de forma autom谩tica, siempre y cuando tengas dicho componente abierto.

Yo tome un enfoque un tanto particular.

App.js

 import React from "react";
import "./App.css";
import Header from "./components/Header";
import "react-bootstrap";
import "bootstrap/dist/css/bootstrap.min.css";

function App() {
  return (
    <div className="App">
      <div>
        <Header />
      </div>
    </div>
  );
}

export default App;

header.jsx

<code> import React, {useState} from 'react'
import Characters from "./Characters";

const Header = () => {
 const  [darkMode,setDarkMode] = useState(false);
 const handleClick = () => {
     setDarkMode(!darkMode);
     
     const candleButton = document.querySelector(".App");
     const title = document.querySelector(".Header__title");
     const card = document.getElementsByClassName("card");


     candleButton.classList.toggle("dark-background");
     title.classList.toggle("dark-title");
     
     for (var i = 0; i<card.length; i++) {
        card[i].classList.toggle("dark-card");        
     }
 }
  return (
    <>
    <div className="Header container">
      <div className="Header_container">
        <h1 className="Header__title">React Hook</h1>
        <button type="button" className="btn btn-primary" onClick={handleClick} >{darkMode ? 'Dark Mode':'Light Mode'}</button>
      </div>
      <Characters />
     </div>
    </>
  )
}

export default Header; 

Characters,jsx

import React,{useState,useEffect} from 'react'

const Characters = () => {

const [characters, setCharacters] = useState([]);

useEffect(
    ()=>{
    fetch('https://rickandmortyapi.com/api/character?page=2')
    .then(response => response.json())
    .then(data => setCharacters(data.results))
},[]);

return (
    <>
    <div className="Characters">
    <div className="card-group ">

        {characters.map(characters =>(
                <div className="card" key={characters.id}>
                    <img src={characters.image} className="card-img-top" alt={characters.name}/>
                    <div className="card-body">
                    <h5 className="card-title">{characters.name}</h5>
                    <p className="card-text"><small className="text-muted">{characters.status}</small></p>
                    </div>
              </div>
        ))}
    </div>
    </div>

    </>
  )
}

export default Characters;

Reto cumplido !

Cost贸 pero sali贸鈥

Para hacer el reto utilice la librer铆a de classNames, que funciona para anidar una clase de CSS a un elemento, siempre y cuando se cumpla una condici贸n. Adem谩s necesite hacer un levantamiento de estado, para que el componente padre de APP tuviera acceso al estado de Darkmode, que cambia al presionar el bot贸n en el header.
No me centre mucho en el dise帽o, mas que todo quer铆a lograr la funcionalidad y este fue el resultado:

Y ac谩 dejo el c贸digo de como hice el levantamiento de estado y utilice la librer铆a de classname:

![Screenshot_3.png]https://static.platzi.com/media/user_upload/Screenshot_3-90bacbb5-130f-4dfa-86e3-7616c78c9f49.jpg)

Yo hice el fetching de datos usando facilmente Graphql y Apollo con estas lineas de codigo

  1. Instalas graphql y apolloclient con
npm install @apollo/client graphql
  1. Luego creas un cliente, un archivo comun y corriente que se conectara a la api de rickymorty pero con grapql, algo asi

    lo exportamos y le ponemos la configuracion al endpoin de graphql y el cache
  2. Y por ultimo en nuestro componente con el useEffect hacemos la consulta con nuestro cliente, obtenemos los datos y buala! Asi en ves de Rest con Graphql solo traemos los datos que necesitamos

Bueno para agregar el DarkMode y LightMode agrege estas lineas de codigo en el Componente Header.

const Header = () => {
  const [mode, setMode] = useState(false)
  
  const onHandleClick= ()=>{
    setMode(!mode);
  } 

  if(mode){
    document.documentElement.style.setProperty('--background-App','#282c34')
    document.documentElement.style.setProperty('--font-color','white')
  }else{
    document.documentElement.style.setProperty('--background-App','white')
    document.documentElement.style.setProperty('--font-color','black')
    
  }
  
  return (
    <div className='header'>
      <h1>React Hooks</h1>
      <button type='button' onClick={onHandleClick} >{mode ? "DarkMode" : "LigthMode"}</button>
    </div>
  );
}
export default Header


El resultado:

En vez de usar la API de Rick and Morty estoy usando la API de Marvel:
https://developer.marvel.com/docs

Dark Mode:

Reto cumplido!


Creo que hace falta una explicaci贸n mas extensa me quedan mucho vacios que logre solucionar con el siguiente video que les comparto
https://www.youtube.com/watch?v=0_D8ruGVp20&ab_channel=Appdelante

Reto completado 馃槃

useEffect no es m谩s que una funci贸n que se ejecuta autom谩ticamente cuando nuestro componente se renderiza y/o actualiza

usar useEffect es ahorrarse 2 methods del ciclo de vida de un componente, a saber, componentDidMount y componentDidUpdate.

Interesante Reto

Comparto el reto y codigo de c贸mo lo realic茅:

App.js

import React, { useState } from 'react';
import Header from './components/Header';
import Characters from './components/Characters';
import './App.css';

function App() {
  const [darkMode, setDarkMode] = useState(false);    
  const handleClick = () => {
      setDarkMode(!darkMode);
  }
    
  return (
    <div className={`App ${darkMode ? 'dark-mode' : 'light-mode'}`}>
      <Header />
      <div>
        <button type="button" onClick={handleClick}>
          { darkMode ? 'Change to LightMode' : 'Change to DarkMode' }
        </button>
      </div>  
      <Characters />
    </div>
  );
}

export default App;

Characters.jsx

import React, { useState, useEffect } from 'react'

const Characters = () => {
    const [characters, setCharacters] = useState([])

    const getCharacters = async () => {
        const response = await fetch('https://rickandmortyapi.com/api/character/');
        const data = await response.json();
        const results = data.results;
        setCharacters(results);
        console.log('results ->', results);
    }

    useEffect(() => {
        getCharacters();
    }, [])

    return (
        <div className="">
            <div className="Characters">
                {
                    characters.map( (character) => (
                        <div className="Character" key={character.name}>
                            <h2>{ character.name}</h2>
                            <figure>
                                <img src={character.image} alt={character.species} />
                                <figcaption>
                                    <p><b>Gender: </b> {character.gender}</p>
                                    <p><b>Origin: </b> {character.origin.name}</p>
                                    <p><b>Location: </b> {character.location.name}</p>
                                    <p><b>Specie: </b> {character.species}</p>
                                    <p><b>Status: </b> {character.status}</p>
                                </figcaption>
                            </figure>
                        </div>
                    ))
                }
            </div>
        </div>
    );
};

export default Characters;

index.css

/*Init by React JS */
body {
  margin: 0;
  padding: 0;
  font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
    'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
    sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
}

code {
  font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New',
    monospace;
}
/*set the modes of views */
.dark-mode { background: #201e1e; border: 1px solid #fafafa; }
.dark-mode h1, 
.dark-mode h2, 
.dark-mode p { color: #fafafa; }
.dark-mode p b { color: #f1d248; }
.dark-mode .Character { border: 1px solid #fafafa; }
.light-mode { background: #fafafa; border: 1px solid #201e1e; }
.light-mode h1, 
.light-mode h2,
.light-mode p { color: #201e1e; }
.light-mode .Character { border: 1px solid #201e1e; }

/*Painting the views */
.App button {
  background-color: darkgoldenrod;
  border: none;
  padding: .3rem;
  border-radius: 8px;
}:focus { outline: none; }

.Characters {
  width: 100%;
  margin: 0 auto;
  display: flex;
  flex-flow: row wrap;
  justify-content: center;
  box-sizing: border-box;
}
.Character {
  margin: .1rem .1rem;
  border: 1px solid black;
}
.Character figure img {
  width: calc(100%);
}

Espero les pueda ayduar de gu铆a. Ac谩 el resultado:

darkmode con tailwind

Me hice un componente simple usando sass :B

import React, { useState, useEffect } from 'react';
import '../styles/Characters.sass';

const Characters = () => {
	const [Characters, setCharacters] = useState([]);
	useEffect(() => {
		fetch('https://rickandmortyapi.com/api/character/')
			.then(res => res.json())
			.then(data => setCharacters(data.results));
	}, []);
	return (
		<div class="container">
			{Characters.map(character => (
				<div className="card">
					<figure>
						<img src={character.image} alt="" />
					</figure>
					<h2>{character.name}</h2>
					<p>{character.status}</p>
					<p>{character.species}</p>
					<p>{character.gender}</p>
					<p>{character.location.name}</p>
				</div>
			))}
			<h2>{}</h2>
		</div>
	);
};

export default Characters;

.container
	display: flex
	flex-wrap: wrap
	justify-content: center
.card
	padding: 4%
	text-align: start
	margin: 10px
	border-radius: 10px
	box-shadow: 1px 1px 9px 1px #1d010125
	figure
		img
			width: 100px

n

hola chicos, alguno descargo los archivos e intento correrlo? escribi en terminal npm start, npm run start y en ninguno corrio 馃槮

Reto completado

Observaci贸n: Falt贸 establecer un key para cada elemento interado.

Nota sobre useEffect
Al pasar como segundo par谩metro de useEffect un arreglo vac铆o quiere decir que useEffect se ejecutar谩 solo cuando se monte el componente, si queremos que se ejecute cada vez que se actualice el componente no pondremos ning煤n par谩metro. Si queremos que se actualice cada vez que algo cambie, dentro del arreglo pondremos dicho par谩metro a escuchar, y si queremos hacer algo cuando se desmonte el componente retornaremos esa acci贸n en la funci贸n de useEffect.

Para usar los atajos de los que habla el profesor, hay que instalar la siguiente extensi贸n, ya que la que puso en los links de apoyo no contiene estos atajos 馃槂

https://marketplace.visualstudio.com/items?itemName=EQuimper.react-native-react-redux

Mi soluci贸n al reto 馃挴
Light Mode:

Dark Mode:

Dark:

Decidi hacerlo con una api de la Nasa
PD: son imagenes de Marte

He aqu铆 mi versi贸n 馃槃

compa帽eros si les aparece este error:

Warning: Each child in a list should have a unique "key" prop.

recuerden solucionarlo de esta manera poniendo un id unico a cada uno.

<h2 key={character.id}>{character.name}</h2>

As铆 me quedo xD

Aqu铆 mi aporte, pero tengo una duda. C贸mo hago para que desde mi componente Header pueda pasar el estado del dark/ligth hasta el componente padre para seg煤n este valor hacer lo efectos? Tengo los componentes Header y Characters que los llamo en el index. (No s茅 si me hago entender). Otra forma ser铆a de sacar todo el c贸digo del componente Header hasta el index y ah铆 se har铆a facil el manejo, pero a mi forma de ver no es el correcto. Adjunto pantalla de la p谩gina index y la captura del resultado del api en modo ligth.


Me tomo mas tiempo de lo que pens茅, pero aqu铆 esta.

Nunca antes hab铆a hecho un Dark Mode. Me tom贸 m谩s tiempo del esperado, pero quedo satisfecho con lo que aprend铆. Aqu铆 mis resultados:

Decid铆 usar una API que encontr茅 medio hecha en Github, la complet茅 con Express y la 鈥減agin茅鈥, despu茅s la llam茅 desde mi Front con ReactHooks, este es el LightMode:

Y este es el DarkMode:

Por cierto, modifiqu茅 un poco la llamada a la API porque estoy usando la gu铆a de estilos de Airbnb y eslint para tener un poco m谩s limpio mi c贸digo, este es el Characters.jsx

import React, { useState, useEffect } from 'react';
import Card from './Card';

const Characters = () => {
  const [characters, setCharacters] = useState([]);
  const [page, setPage] = useState(1);
  useEffect(async () => {
    const response = await fetch(`${process.env.REACT_APP_API}/characters/page/${page}`);
    const data = await response.json();
    setCharacters(data.characters);
    setPage(page + 1);
  }, []);
  return (
    <div className="Characters row">
      {
        characters.map((character) => (
          <div className="col-4" key={character.id}>
            <Card
              header={character.name}
            >
              <>
                <img src={character.image} alt="character" />
                <p>{character.description}</p>
              </>
            </Card>
          </div>
        ))
      }
    </div>
  );
};

export default Characters;

Y mi Card.jsx:

import React from 'react';
import PropTypes from 'prop-types';

const Card = ({ header, children, footer }) => (
  <div className="Card">
    <div className="Card-header">
      {header}
    </div>
    <div className="Card-content">
      {children}
    </div>
    <div className="Card-footer">
      {footer}
    </div>
  </div>
);

Card.propTypes = {
  header: PropTypes.string,
  children: PropTypes.element,
  footer: PropTypes.string,
};

Card.defaultProps = {
  header: 'No name',
  children: <div />,
  footer: 'No description',
};

export default Card;

Holy Cow, That麓s useFul !

Mi resultado

Modo claro:

Modo oscuro:

El cambio de estilos se me hizo bastante duro, pero gracias a un ejemplo pude hacer algo parecido, aunque me hubiera gustado manipular de mejor manera el DOM

Algun consejo para poder manipular cualquier estilo de cualquier componente?

Este fue mi resultado鈥

useEffect: Funci贸n que recibe dos par谩metros, el primer par谩metro es una funci贸n an贸nima que manejara la l贸gica, y el segundo es una variable que estar谩 escuchando en el caso que tenga un cambio, esto es similar al ciclo de vida que us谩bamos en la clases

As铆 qued贸 mi proyecto jeje:

Muy r谩pido y usable, no hab铆a entendido hasta ahora