Introducción al curso avanzado de React

1

Qué necesitas para este curso y qué aprenderás sobre React.js

2

Proyecto y tecnologías que usaremos

Preparando el entorno de desarrollo

3

Clonando el repositorio e instalando Webpack

4

Instalación de React y Babel

5

Zeit es ahora Vercel

6

Linter, extensiones y deploy con Now

Creando la interfaz con styled-components

7

¿Qué es CSS-in-JS?

8

Creando nuestro primer componente: Category

9

Creando ListOfCategories y estilos globales

10

Usar información real de las categorías

11

Creando PhotoCard y usando react-icon

12

SVGR: de SVG a componente de ReactJS

13

Creando animaciones con keyframes

Hooks

14

¿Qué son los Hooks?

15

useEffect: limpiando eventos

16

useCategoriesData

17

Usando Intersection Observer

18

Uso de polyfill de Intersection Observer e imports dinámicos

19

Usando el localStorage para guardar los likes

20

Custom Hooks: useNearScreen y useLocalStorage

GraphQL y React Apollo

21

¿Qué es GraphQL y React Apollo? Inicializando React Apollo Client y primer HoC

22

Parámetros para un query con GraphQL

23

Usar render Props para recuperar una foto

24

Refactorizando y usando variables de loading y error

25

Usando las mutaciones con los likes

Reach Router

26

¿Qué es Reach Router? Creando la ruta Home

27

Usando Link para evitar recargar la página

28

Creando la página Detail

29

Agregando un NavBar a nuestra app

30

Estilando las páginas activas

31

Rutas protegidas

Gestión del usuario

32

Introducción a React.Context

33

Creación del componente UserForm; y Hook useInputValue

34

Estilando el formulario

35

Mutaciones para registro

36

Controlar estado de carga y error al registrar un usuario

37

Mutaciones para iniciar sesión

38

Persistiendo datos en Session Storage

39

Hacer like como usuario registrado

40

Mostrar favoritos y solucionar fetch policy

41

Cerrar sesión

Mejores prácticas, SEO y recomendaciones

42

Últimos retoques a las rutas de nuestra aplicación

43

React Helmet

44

Midiendo el performance de nuestra app y usando React.memo()

45

React.lazy() y componente Suspense

46

Usando PropTypes para validar las props

47

PWA: generando el manifest

48

PWA: soporte offline

49

Testing con Cypress

Conclusiones

50

¡Felicidades!

No tienes acceso a esta clase

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

Curso de React Avanzado

Curso de React Avanzado

Miguel Ángel Durán

Miguel Ángel Durán

useEffect: limpiando eventos

15/50
Recursos

Aportes 73

Preguntas 12

Ordenar por:

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

o inicia sesión.

Les dejo un artículo que escribí sobre el hook useEffect por si les puede ayudar a complementar esta clase: https://midu.dev/react-hooks-use-effect-funcionalidad-en-el-ciclo-vida-componentes/

Yo en vez de hacer un fadeIn hice que se mostrara desde arriba. También aproveché la animación para no renderear el componente cada vez que el estadoshowFixed cambie si no que la animación se encargue de sacarlo del viewport.

Usando react spring 😛

un pequeño shortcode para usar los Fragments de react es usar " <>" y “</>” para cerrar. No hace falta importar {Fragment} de react, ya que ya lo interpreta.

    <>
      {renderList()}
      {renderList(true)}
    </>

Les dejo esta animación de efecto rebote

const bounceDownKeyFrames = keyframes`
 0% {
    top: -70px;
  }

  25% {
    top: 0px;
  }

  40%{
    top: 10px
  }

  65%{
    top: -3px
  }

  100% {
    top: 0px;
  }
`

export const bounceDown = ({ time = '1s', type = 'ease' } = {}) =>
  css`
    animation: ${time} ${bounceDownKeyFrames} ${type};
  `

Solo agregue la funcion de fadeIn en styles de la lista, para cuando es fixed:

import styled from 'styled-components'
import { fadeIn } from '../../styles/animation'

export const List = styled.ul`
  display: flex;
  overflow: scroll;
  width: 100%;
  &.fixed {
    ${fadeIn()};
    background-color: #fff;
    border-radius: 60px;
    box-shadow: 0 0 20px rgba(0, 0, 0, 0.3);
    left: 0;
    margin: 0 auto;
    max-width: 400px;
    padding: 5px;
    position: fixed;
    right: 0;
    top: -20px;
    transform: scale(.5);
    z-index: 1;
  }
`

export const Item = styled.li`
  padding: 0 8px;
`

Lista fija de categorías con la animación fadeIn que habíamos hecho:

hasta ahora pensaba que solo podía usar usar useEfect una sola vez en un componente 🤯🤯🤯

Con algunos comentarios de como entendí:

import React, { Fragment, useEffect, useState } from "react";
import { Category } from "../Category/Category";
import { List, Item } from "./styles";

export const ListOfCategories = () => {

  /* setear el estado inicial */
  const [categories, setCategories] = useState([]);
  const [showFixed, setShowFixed] = useState(false);

  /* useEffect se ejecuta cada vez que se renderiza el componente */

  /* useEffect para obtener los datos del API */
  useEffect(function () {
    window
      .fetch("http://localhost:3000/categories")
      .then((res) => res.json())
      .then((response) => {
        setCategories(response);
      });
  }, []);


  /* useEffect para mostrar las categorias cuando el scroll baje */
  useEffect(function () {

    /* Función onScroll para verificar el movimiento del scroll */
    const onScroll = e => {
      /* si el scroll  es en Y es mayor a 200 entones newShowFixed == true */
      const newShowFixed = window.scrollY > 200

      /* si el */
      showFixed !== newShowFixed && setShowFixed(newShowFixed)
    }

    /* Escuchar el movimiento del scroll, se suscribe un evento. */
    document.addEventListener('scroll', onScroll)
    console.log(showFixed);

    /* cuando se desmonta el componente se elimina la suscripción al evento */
    return () => document.removeEventListener('scroll', onScroll)

  }, [showFixed])


  const renderList = (fixed) => (
    <List className={fixed ? 'fixed' : ''}>
      {categories.map((category) => (
        <Item key={category.id}>
          <Category {...category} />
        </Item>
      ))}
    </List>
  )
  return (
    <Fragment>
      {renderList()}
      {showFixed && renderList(true)}
    </Fragment>

  );
};

Mi propuesta para hacer el efecto de las categorias con IntersectionObserver 🤗🤗🤗:

Yo use una animación para cambiar el scale, dejo el snippet por si alguien quiere probarlo.

const scaleDown = keyframes`
    from {
      transform: scale(1);
    }
    to {
      transform: scale(0.5);
    }
`;
const scale = ({time = "1s", type = "ease"} = {}) => css`animation: ${time} ${scaleDown} ${type}`;

const Ul = styled.ul`
  display: flex;
  overflow: scroll;
  width: 100%;
  ${props => props.fixed && css`
    background: #ffffff;
    border-radius: 60px;
    box-shadow: 0 0 20px rgba(0, 0, 0, 0.3);
    left: 0;
    margin: 0 auto;
    max-width: 500px;
    padding: 5px;
    position: fixed;
    right: 0;
    top: -20;
    transform: scale(0.5);
    ${scale}
    z-index: 1;
  `}
`;

Excelente…

Acá mi animación:


const positionDownKeyFrames = keyframes`
    0%{
      margin-top:-100px;
    }

    100%{
      margin-top:0;

    }
`
export const positionDown = ({ time = '1s', type = 'ease' } = {}) =>
  css`animation: ${time} ${positionDownKeyFrames} ${type}`
Me parece que este problema se hubiese solucionado con la propiedad "sticky" de css. Aunque esta clase sirvió para aprender otras cosas interesantes, pero estaría bueno haber dejado claro eso.

De esta manera hice el efecto (con una ternary):

useEffect(function () {
    function onScroll (e) {
      window.scrollY > 200
        ? setShowFixed(true)
        : setShowFixed(false)
    }

    document.addEventListener('scroll', onScroll)

    return function () {
      document.removeEventListener('scroll', onScroll)
    }
  }, [showFixed])

una lectura para entender mas de useEffect
useEffect en el ciclo de vida de componentes

no se si a alguien mas le paso, pero si no les sale la renderList(fixed) coloquenla antes que la renderList()

<>
{renderList(true)}
{renderList()}
</>

hola !
la url ("https://petgram-server.midudev.now.sh/categories") para obtner las cetegorias esta devolviendo un 502

Aqui esta mi animacion espero les guste

Para los que viene de los cursos de oscar 😛 tambien podiamos indicar los fragment de esta manera “corta” <> </>

  return (
    <>
      {renderList()}
      {showFixed && renderList(true)}
    </>
  )

¿Es necesario usar doble llamado a la función renderList? ¿No es suficiente con algo así?

renderList(showFixed)

me surge una duda

const renderList = () => { ... }

no termina creando una nueva función en cada render del componente? o es algo por lo que no debería de preocuparme?

Cada vez voy notando el poder de utilizar styled-components en nuestros proyectos, incluso lo podemos usar para condicionar nuestro estado, excelente herramienta. 🚀…

Increíble 😱, llevo varios proyectos incorporando reactHooksy nunca se me había pasada por la cabeza hacer más de un useState, me acostumbre a resalizar solo 1 componentDidMount, pero me doy cuenta que el useState combina los poderes del componentDidUpdateen el array que recibe como segunda parámetro, es increíble, ahora mis aplicaciones tendrán un mejor rendimiento. 😄

Puedes describir como hacer un correcta estructura de archivos y carpeta para un proyecto?

Implementación del efecto fadeIn en la lista de categorias

import styled from 'styled-components'
import { fadeIn } from '../../styles/animation'

const List = styled.ul`
  display: flex;
  overflow-x: auto;
  width: 100%;
  &.fixed {
    ${fadeIn({ time: '0.8s', type: 'ease-in-out' })}
    background: #fff;
    border-radius: 60px;
    box-shadow: 0 0 20px rgba(0, 0, 0, 0.3);
    left: 0;
    margin: 0 auto;
    max-width: 400px;
    padding: 5px;
    position: fixed;
    right: 0;
    top: -20px;
    transform: scale(0.5);
    z-index: 1;
  }
`

const Item = styled.li`
  padding: 0 8px;
`

export { List, Item }
mm me parece que useffect donde se setea el listener de scroll, no debería tener ninguna dependencia, ya que se debe ejecutar solo la primera vez que se renderiza el componente, no? No tiene sentido que se vuelva a ejecutar cada vez que se renderiza, ya que el callback "onScroll" se encarga de eso

Usando react-top-loading-bar

Mi animación

Code:
animation.js

import styled, { css, keyframes } from "styled-components";

const scale = keyframes`
from {
  transform: scale(1);
  top: -100px;
  border-radius: 2px;
}
to{
  transform: scale(.66);
  border-radius: 60px;
}
`
export const scaleDown = ({ time = "1s", type = "ease" } = {}) => css`
animation: ${time} ${scale} ${type};
`;

que gran curso! lastima que necesite actualizacion

Para el reto solo importamos en los styles:
import { fadeIn } from ‘…/…/styles/animation’

Luego agregamos: ${fadeIn()} cuando se haga el fixed.
&.fixed {
${fadeIn()}
.
.
.
}

Les dejo otra forma utilizando Logical Operators y un video de @Midudev explicando sobre el uso de ternarias: https://www.youtube.com/watch?v=YFES8Nm6uF4

<List className={fixed &&= 'fixed'}>
//Code
</List>

Acá hay otra forma de hacer el toggle de las categorías:

  useEffect(function () {
    const onScroll = e => {
      window.scrollY > 200 ? setShowFixed(true) : setShowFixed(false)
    }
    document.addEventListener('scroll', onScroll)
  }, [])

Yo hice la comprobacion de esta manera para no hacer otra funcion

useEffect(() => {
    const handleScroll = () => {
      if (window.scrollY > 200) {
        setFixedCategory(true);
      } else {
        setFixedCategory(false);
      }
    };
    window.addEventListener("scroll", handleScroll);
    return () => {
      window.removeEventListener("scroll", handleScroll);
    };
  }, [fixedCategory]);

  return (
    <List className={fixedCategory ? "fixed" : null}>
      {categories?.map((category) => (
        <li key={category.id}>
          <Category {...category} />
        </li>
      ))}
    </List>
  );
};
export { ListOfCategories };

Hola!, el código en la descripcion de la clase aparece minificado. Al menos el index.js de ListOfCategories

Hola aquí les dejo mi aporte:

No es malo estar agregando eventos en cada render de la app?

Asi me quedo a mi, el escrolleable era un objeto del dom y no el window

import React,{ useEffect, useState } from "react"
import   styled     from "styled-components"
import { Category } from "../Category"
import   db         from "../../backend/api/db.json"

/// if use trick declare object:  const renderList =(fixed, {delay: delay}=0 )=>
const renderList =(fixed, delay=0 )=>   {   return ( <>
  <List className={ fixed? 'fixed' : '' }>
    { db.categories.map( 
        cat=>
          <Item key={cat.id}> 
            <Category {...cat} delay={delay}/>
          </Item> 
      ) 
    }
  </List>
</>
)};

const ListOfCategories =()=>{ const [showFixed, setShowFixed ] = useState(false)

  useEffect(()=> {
    const cards = document.querySelector("#scrolleable");
    
    const onScroll = (cards=this.cards, e) =>{
      const newShowFixed = cards.scrollTop>100;
      showFixed !== newShowFixed && setShowFixed(newShowFixed);

    }; cards.addEventListener('scroll', ()=>onScroll(cards) );

    return ()=> cards.removeEventListener('scroll', onScroll);
  },[showFixed])
  
  return (
    <> 
      {renderList()}
      {showFixed && renderList(true)} 
      {/* {showFixed && renderList(true, {delay=10} )}  */} //!trick
    </> 
  ) 

}; export { ListOfCategories, renderList };

export const List = styled.section`
  display: flex;
  position: relative;
  z-index: 1;
  overflow: auto;
  width: 100%;
  height: 130px;
  ::-webkit-scrollbar {
    display: none;
  }
  &.fixed{
    border-radius: 1000px;
    box-shadow: 0 0 20px rgba(0,0,0,.40);
    position: sticky;
    top: -36px;
    right: 0;
    left: 0;
    margin: 0 auto;
    max-width: 400px;
    padding: 5px;
    transform: scale(.5);
    z-index: 13;
    backdrop-filter: blur(30px);
  }
`
export const Item = styled.article`
  padding: 0 8px;
`

ListOfCategories/index.js

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

import {List, Item} from './styles';

import { Category } from '../Category';

export const ListOfCategories = () => {
    const [categories, setCategories] = useState([]);

    const [showFixed, setShowFixed] = useState(false);

    //Show Categories fetching the data
    useEffect(function(){
        window.fetch('https://petgram-server-alejandroverita-alejandroverita.vercel.app/categories')
            .then(res => res.json())
            .then(data => {
                setCategories(data)
        })
    }, [])

    //Show categories when scroll is higher 200px
    useEffect(()=>{
        const onScroll = e => {
            const newShowFixed = window.scrollY > 200
            showFixed !== newShowFixed && setShowFixed(newShowFixed)
        }

        document.addEventListener('scroll', onScroll)

        //Avoid memoryLeak
        return () => document.removeEventListener('scroll', onScroll)
    }, [showFixed])

    const renderList = (fixed) => (
        <List className={fixed ? 'fixed' : ''}>
            {
                categories.map(category => 
                    <Item key = {category.id}>
                        <Category {...category} />
                    </Item>
                )
            }
        </List>
    )
    
    return (
        <>
            {renderList()}
            {showFixed && renderList(true)}
        </>
    )
}

ListOfCategories/styles.js

import styled from 'styled-components';
import {scale} from '../../Styles/animation';

export const List = styled.ul`
    display: flex;
    overflow: scroll;
    width: 100%;
    //desaparece el scroll sin perder la funcionalidad 
    &::-webkit-scrollbar {
        display: none;
    };
    &.fixed {
        background-color: white;
        border-radius: 60px;
        box-shadow: 0 0 20px rgba(0, 0, 0, 0.3);
        left: 0;
        margin: 0 auto;
        max-width: 400px;
        padding: 5px;
        position: fixed;
        right: 0;
        top: -20px;
        transform: scale(0.5);
        ${scale({time:'0.3s'})}
        z-index: 1;
    }

  `

export const Item = styled.li` 
    padding: 0 8px;
    list-style: none;
`

animation.js

import {keyframes, css} from 'styled-components';

const fadeInKeyframes = keyframes ` 
    from {
        filter: blur(5px);
        opacity: 0;
    }
    to {
        filter: blur(0);
        opacity: 1;
    }
`;

export const fadeIn = ({time = '1s', type='ease'} = {}) => css`
    animation: ${time} ${fadeInKeyframes} ${type};
`;



const scaleDown = keyframes`
    from {
      transform: scale(1);
    }
    to {
      transform: scale(0.5);
    }
`;

export const scale = ({time='1s', type='ease'} = {})=> css ` 
    animation: ${time} ${scaleDown} ${type};
`;

Para un mayor entendimiento del código, aconsejo sacar la función onScroll del useEffect… porfas, si hacer esto está mal, háganmelo saber


  const onScroll = (e) => {
    const newShowFixed = window.scrollY > 200

compare with showFixed
    showFixed !== newShowFixed && setShowFixed(newShowFixed)
newShowFixed before is declarated
    console.log('adios:(')
  }

  useEffect(() => {
    document.addEventListener('scroll', onScroll)
    return () => {
      console.log('hola:)')
      document.removeEventListener('scroll', onScroll)
    }
  }, [showFixed])

Tenia problemas con ejecutar el listener y traer el valor de window.scrollY. No se ejecutaba ninguno de los dos. Leyendo me encontre con que en los estilos globales al id #app le estamos pasadano un height: 100vh. Una vez lo quité, me funciono correctamente la accion de lista de categorias. Alguien me puede explicar por que?

#app{
    box-shadow: 0 0 10px rgba(0,0,0,.05);
    overflow-x: hidden;
    //height: 100vh;  // si se le da esta propiedad, no se puede acceder a window.scrollY ni document.addEventListener('scroll'...
    padding-bottom: 10px;
  }

Genial

ni idea que existia transform: scale(.5); jajaj

Les comparto un proyecto que hice, consumiendo una API https://petlove-3sbf43kkw-montilva74.vercel.app/, no es perfecto pero me siento orgullosa de todo.

const onScroll = () => {
      window.scrollY > 200 ? setShowFixed(true) : setShowFixed(false);
    };

Muy interesante, muchas gracias!!

Lo que no entendí al principio era el llamado a Vercel, pero ya en materia sólo tenía que llamar a la api:

https://petgram-server-rsor.vercel.app/categories

Excelente, cosas que antes pensaba que eran tediosas de hacer en React ahora son simples.

Al crear la lista estática esta se muestra debajo de la lista fhtoCard, ¿como puedo solucionarlo?

Lista de categorias con el efecto fadeIn
https://ibb.co/3Mcb1d6

http://platzi.com/comentario/660554/
Al darle scroll para mostrar la lista estatica en mi proyecto esta se muestra por debajo de la lista fhotocard

@midudev como puedo usar variables de elementos dentro de otro elemento? Te hago un ejemplo:

Para estilos de categorias

export const Anchor = styled.a`
  display: flex;
  flex-direction: column;
  text-align: center;
  text-decoration: none;
  width: 75px;
  &.fixed {
    width: 45px;
  }
`
export const Image = styled.img`
  border: 1px solid #ddd;
  box-shadow: 0px 1px 14px rgba(0, 0, 0, .2);
  border-radius: 50%;
  height: auto;
  overflow: hidden;
  object-fit: cover;
  height: 75px;
  width: 75px;
`

Y teniendo otro elemento que sea el contenedor de los otros dos, por ejemplo

export const CategoryContainer = styled.div`
  &.fixed {
    a {
      width: 45px;
    }

    img {
      width: 45px;
      height: 45px;
    }
  }
`

¿Cómo puedo hacer para que en vez de utilizar a, img, utilizar las constantes asociadas a ese elemento? Es decir:

export const CategoryContainer = styled.div`
  &.fixed {
    Anchor {
      width: 45px;
    }

    Image {
      width: 45px;
      height: 45px;
    }
  }
`

¿Como puedo lograr algo similar?

Saludos Miguel, muy buen curso!.
Tengo una consulta: tengo un custom Hook llamado useFetch() que quiero usar como helper para todas las peticiones que necesite el sitio, el problema surge cuando lo quiero utilizar dentro de un useEffect, bien sea para una carga inicial o un pulling con interval, entiendo que es por las reglas de los Hooks.
La duda es, no es un caso correcto de use Hooks? es decir, debo reescribirlo como una funcion helper o estoy haciendo algo mal?
Muchas gracias.!

¡Geniaaal! Maravilloso efecto y super simple!
Solo tengo una duda y quizá sea ridícula, pero aún así no me privaré de expresarla @midudev ¿No hay problemas de performance al agregar el listener cada que se renderiza el componente? ¿O el browser se da cuenta que el listener en realidad no hay cambiado y lo gestiona?
Pero enserio me pareció brutal lo que hiciste por que mi solución hubiese sido mucho más compleja, ¡vivan los hooks!

Una pregunta, al desmontar el componente ¿Los addeventlisteners se borran?

import styled from 'styled-components';

import fadeIn from '../../styles/animation';

export const Anchor = styled.a`
  display: flex;
  flex-direction: column;
  text-align: center;
  text-decoration: none;
  width: 75px;
`;

export const Image = styled.img`
  ${fadeIn()}
  border: 1px solid #ddd;
  box-shadow: 0px 10px 14px rgba(0, 0, 0, .2);
  border-radius: 50%;
  height: auto;
  overflow: hidden;
  object-fit: cover;
  height: 75px;
  width: 75px;
`;

No se que tan buena sea mi solución, pero hice el reto agregando que se haga la animación cuando aparece y cuando desaparece la lista de categorías.

src/components/ListOfCategories/styles.js
agregue las animaciones
propiedad visibility
el transition
importo animación fadeOut
y la variable showOrHide que paso por las props

import { fadeIn, fadeOut } from '../../styles/animations'

export const List = styled.ul`
  display: flex;
  overflow: scroll;
  width: 100%;
  &.fixed{
    ${props => props.showOrHide ? fadeIn() : fadeOut()}
    visibility: ${props => props.showOrHide ? 'visible' : 'hidden'};
    background: #fff;
    border-radius: 60px;
    box-shadow: 0 0 20px rgba(0, 0, 0, 0.3);
    left: 0;
    margin: 0 auto;
    max-width: 400px;
    padding: 5px;
    position: fixed;
    right: 0;
    top: -20px;
    transform: scale(.5);
    z-index: 2;
    transition: visibility 1s;
  }
`

src/styles/animations.js
la animación fadeOut

const fadeOutKeyframes = keyframes`
  from{
    filter: blur(0);
    opacity: 1;
  }

  to {
    filter: blur(5px);
    opacity: 0;
  }
`
export const fadeOut = ({ time = '1s', type = 'ease' } = {}) =>
  css`animation: ${time} ${fadeOutKeyframes} ${type};`

src/components/ListOfCategories/index.js
siempre se renderizan las 2 listas de categorías
el estado showFixed lo paso al componente en la propiedad showOrHide

const renderList = (fixed) => {
    return (
      <List showOrHide={showFixed} className={fixed ? 'fixed' : ''}>
        {
          categories.map(category => <Item key={category.id}><Category {...category} /></Item>)
        }
      </List>
    )
  }

  return (
    <>
      {renderList()}
      {renderList(true)}
    </>
  )

esta parte esta bien interesante
https://petgram.flavs.now.sh/
aqui pueden ver mi resultado
solo agregue en:
&.fixed
${fadeIn()}
ease;
y queda ya que los estilos estan definidos

Puse el fadeIn antes de que lo dijera en el final 😯

Mi solucion:
src\components\ListOfCategories\Styles.js

import styled from 'styled-components'
import { fadeIn } from '../../Styles/animation'

export const List = styled.ul`
  display: flex;
  overflow: scroll;
  width: 100%;
  &::-webkit-scrollbar {
    display: none;
  }
  &.fixed {
    ${fadeIn({ time: '1s' })}
    background: #FFF;
    border-radius: 60px;
    box-shadow: 0 0 20px rgba(0, 0, 0, 0.3);
    left: 0;
    margin: 0 auto;
    max-width: 400px;
    padding: 5px;
    position: fixed;
    right: 0;
    top: -20px;
    transform: scale(0.5);
    z-index: 1;
  }
`
export const Item = styled.li`
  padding: 0 8px;
`

NO entendi esta parte

    document.addEventListener('scroll', onScroll)
    return () => document.removeEventListener('scroll', onScroll)
  }, [showFixed])```

no me quedó muy claro lo de las dependencias… y en la documentación no lo encontré

Por fin pude entender useEffect! Muchas gracias!

Muy sencillo utilizar el fadeIn ya lo teniamos preparado para usar en cualquier otro .css

Custom Hooks
Son infinitos porque podemos crear nuestro propio custom hooks.
Podemos utilizar un hooks de forma que se pueda reutilizar la lógica en diferentes componentes. Otra ventaja es la separación de conceptos que nos permite separar diferentes conceptos en diferentes hooks, agrupando mejor que estamos haciendo en cada parte de nuestro código.
Son 100% retro compatibles, no tenemos que volver a escribir nuestro código para adaptarlo a los hooks pasamos progresivamente nuestra aplicación a utilizar hooks cuando tenga sentido.
Mejor transpilación con Babel. Mejor Performance, la ejecución de una función es más rápida que evaluar una clase.

Dato curioso
Fragment se puede usar de 3 formas

React.Fragment //Usando React para acceder a esta propiedad 
Fragment //Importandolo
<></> //isin importar ni llamar por propiedad

Aplicar el efecto de fadeIn se puede hacer reutilizando la animación usada en las imágenes de las cardas:

export const List = styled.ul`
  display: flex;
  overflow: scroll;
  width: 100%;
  &::-webkit-scrollbar {
    display: none;
  }
  &.fixed {
    ${fadeIn({time: '0.8s'})}
    background: white;
    border-radius: 60px;
    box-shadow: 0 0 20px rgba(0, 0, 0, 0.3);
    left: 0;
    margin: 0 auto;
    max-width: 400px;
    padding: 5px;
    position: fixed;
    right: 0;
    top: -20px;
    transform: scale(.5);
    z-index: 1;
  }
` ```

la animacion cuando aparezca el fixed primero en el styled List pongo esto

  ${({ fixed }) => fixed ? fadeIn() : ''};

luego le paso esa prop

<List fixed className={fixed ? 'fixed' : ''}>

Ya le voy agarrando cariño al useEffect, la aplicación va a al 100

Todavía no logro sacarle la barra de scroll a las categorías. Probé con webkit y si lo saca pero el tema es que el mouse no escrolea horizontalmente. alguna sugerencia?

Hola, les dejo mi repositorio de este proyecto, pero hecho en Nextjs:

https://github.com/danyel117/petgram-platzi/tree/floating_categories

&.fixed{
	${fadeInd({ time: '2s', type: 'ease' })}
}