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

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

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

Regístrate

Comienza en:

3D
20H
42M
59S
Curso de React Avanzado

Curso de React Avanzado

Miguel Ángel Durán

Miguel Ángel Durán

useCategoriesData

16/50
Recursos

Aportes 58

Preguntas 1

Ordenar por:

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

o inicia sesión.

category/index.js

import React from 'react';
import { ContainerCategorySkeleton, CategoryImage, CategoryTitle } from './styles';

export const CategorySkeleton = props => {
    console.log(props)
    return (
        <ContainerCategorySkeleton>
            <CategoryImage light={props.light} />
            <CategoryTitle light={props.light} />
        </ContainerCategorySkeleton>
    )
}

category/style.js

import styled, { css } from 'styled-components';
import { skeletonStyle } from '../../../styles/skeleton';

export const ContainerCategorySkeleton = styled.div`
    display: flex;
    flex-direction: column;
    align-items: center;
`;

export const CategoryImage = styled.div`
    width: 75px;
    height: 75px;
    border-radius: 50%;
    ${
        props => css`
            ${skeletonStyle(props.light)}
        `
    }
`;

export const CategoryTitle = styled.div`
    width: 26px;
    height: 15px;
    margin-top: 8px;
    ${
        props => css`
            ${skeletonStyle(props.light)}
        `
    }
`;

/animation/skeleton/style.js (funciona para cualquier container)

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

const skeletonBackground = (light) => (
    css`
        background: ${ !light
            ? css`linear-gradient(-90deg, #C1C1C1 0%, #F8F8F8 50%, #C1C1C1 100%)`
            : css`linear-gradient(-90deg, #F0F0F0 0%, #F8F8F8 50%, #F0F0F0 100%)`};
            background-size: 400% 400%;
            animation: ${skeletonLoading} 1.2s ease-in-out infinite;
    `
)

const skeletonLoading = keyframes`
    from {
        background-position: 0% 0%;
    }
    to {
        background-position: -135% 0%;
    }
`;

export const skeletonStyle = (light = true) => skeletonBackground(light);

Resultado final:

Dale like si pensaste en meter el <LOADER /> que nos enseño @Sparragus en el curso de react.

😃✌

… así va mi proyecto:

Repo de la clase aquí

Retocando el diseño un poco 😃

Pueden usar nprogress bar para hacer un loading muy rápido y sencillo.

https://loading.io/ <- Aquí hay un montón de Loadings fabulosos

Cree un componente de Loading que llamo desde el la ternaria en el List Component

import React from 'react'
import { SquareLoading, SquareInside } from './styles'

export const Loading = () => {
  return (
    <SquareLoading>
      <SquareInside />
      <SquareInside />
      <SquareInside />
    </SquareLoading>
  )
}

y los estilos así

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

const ldsFacebook = keyframes`
    0% {
        top: 6px;
        height: 51px;
    }
    50%, 100% {
        top: 19px;
        height: 26px;
    }
`

export const SquareLoading = styled.div`
    display: block;
    position: relative;
    width: 64px;
    height: 64px;
    margin: 0 auto 5px;
`
export const SquareInside = styled.div`
    display: inline-block;
    position: absolute;
    left: 6px;
    width: 13px;
    background: #222;
    animation: ${ldsFacebook} 1.2s cubic-bezier(0, 0.5, 0.5, 1) infinite;
    &:nth-child(1) {
        left: 6px;
        animation-delay: -0.24s;
    }
    &:nth-child(2) {
        left: 26px;
        animation-delay: -0.12s;
    }
    &:nth-child(3) {
        left: 45px;
        animation-delay: 0;
    }
`

y listo esto lo llamo desde donde quiera y puedo colocar un loading facil

Yo cree este loader con svg y mucha ayuda de este post
Building a pure CSS animated SVG spinner
 
Aquí también les dejo el CodeSandbox donde lo hice: Simple Loader

Dentro del fetch se puede agregar un Catch del error, por si falla la consulta y fijar el valor de Loading a false, esto evitaria que el loading se quede para siempre.

 fetch(url)
      .then(res => {
        return res.json()
      })
      .then(data => {
        setcategories(data)
        setLoading(false)
      }).catch(error=>{
	setLoading(false)
	}

Tambien agregue un spiner y lo incorpore a las animaciones de la siguiente forma:


const loaderKeyframe = keyframes`
{
  0% {
    transform: rotate(0deg);
  }
  100% {
    transform: rotate(360deg);
  }
}
`
const LoaderAnimation = ({
  time = '1s',
  type = 'linear',
  iteration = 'infinite'
} = {}) =>
  css`
    animation: ${time} ${loaderKeyframe} ${type} ${iteration};
  `

export const Loader = styled.div`
  display: flex;
  width: 100%;
  padding: 20px 0;
  align-items: center;
  justify-content: center;
  &:after {
    ${LoaderAnimation};
    content: ' ';
    display: block;
    width: 46px;
    height: 46px;
    margin: 1px;
    border-radius: 50%;
    border: 5px solid #fff;
    border-color: #69dafd transparent #69dafd transparent;
  }
`

Así se vería mi loading

Hola excelente curso, gracias!!!. No cabe duda que con hooks se simplifican mucho las cosas. No más conect, mapStateToProps… ni cosas raras 😃. Normalmente uso redux-sagas y el setup es una patada en las 🤫

Cargar Categorías

la animación

Este es mi Spinner y aquí encuentras el código.

Agregue un spinner sencillo

Estos son los estilos

const rotateFrames = keyframes`
  0% {
    transform: rotate(0deg);
  }
  100% {
    transform: rotate(360deg);
  }


export const rotate = () => css`
  animation: 1.5s ${rotateFrames} linear infinite;
`

Y este el componente Spinner

export const Spinner = styled.div`
  ${rotate()}
  border: 2px solid violet;
  border-right: 2px solid transparent;
  border-radius: 50%;
  height: 40px;
  margin-bottom: 10px;
  margin-left: calc(50% - 40px);
  width: 40px;
  z-index: 10;
`

así se ve el mio jeje

Se podria aplicar skeleton para el loading

Usando Styled-components use esta pagina https://loading.io/css/ y lo converti. 😄

Para usar la animacion en nuestra lista fija de categorias simplemente debemos importar la funcion fadeIn de animations y usarla cuando se agrega la clase

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

Así esta mi proyecto

`

Para este caso, preferí dar feedback con un icono de loading y rotarlo con una animación. Dejo mi código:

import styled, { css, keyframes } from "styled-components";
import { AiOutlineLoading3Quarters } from "react-icons/ai";

...

const rotate = keyframes`
  from {
    transform: rotate(0deg);
  }

  to {
    transform: rotate(360deg);
  }
`;

...

const Loading = styled(AiOutlineLoading3Quarters)`
  color: #000;
  margin: 0 auto;
  animation: ${rotate} 2s linear infinite;
`;

export {
    ...
    Loading,
};

Esta es la animación para que cargue como Facebook(Skeleton Animation)

const placeHolderShimmer = keyframes`
    0%{
        background-position: -490px 0
    }
    100%{
        background-position: 490px 0
    }
`
export const skeletonAnimation = ({
  time = '1s',
  fill = 'forwards',
  iteration = 'infinite',
  timingFunction = 'linear',
  colorBackground = '#f6f7f8',
  colorAnimation = '#edeef1'
} = {}) =>
  css`
    animation-duration: ${time};
    animation-fill-mode: ${fill};
    animation-iteration-count: ${iteration};
    animation-name: ${placeHolderShimmer};
    animation-timing-function: ${timingFunction};
    background-image: -webkit-gradient(
      linear,
      left center,
      right center,
      from(${colorBackground}),
      color-stop(0.2, ${colorAnimation}),
      color-stop(0.4, ${colorAnimation}),
      to(${colorBackground})
    );
    background-image: -webkit-linear-gradient(
      left,
      ${colorBackground} 0%,
      ${colorAnimation} 20%,
      ${colorBackground} 40%,
      ${colorBackground} 100%
    );
    position: relative;
  `

Hola tengo una pregunta no es mejor que el state del loader empiece en true ya que al cargar la página como bien se dice carga. Se que funciona de igual manera pero para tener más concordancia a la realidad.

Mi ejemplo de loader lo saque de aca https://skeletonreact.com/?ref=morioh.com&utm_source=morioh.com

import React from 'react'
import ContentLoader from 'react-content-loader'

export const MyLoader = (props) => (
  <ContentLoader
    width={500}
    height={100}
    viewBox="0 0 500 100"
    backgroundColor="#f3f3f3"
    foregroundColor="#ecebeb"
    {...props}
  >
    <circle cx="46" cy="38" r="38" />
    <rect x="34" y="83" rx="5" ry="5" width="25" height="10" />
    <rect x="547" y="222" rx="5" ry="5" width="220" height="10" />
    <rect x="82" y="150" rx="5" ry="5" width="220" height="10" />
    <circle cx="137" cy="38" r="38" />
    <rect x="124" y="83" rx="5" ry="5" width="25" height="10" />
    <circle cx="228" cy="38" r="38" />
    <rect x="215" y="83" rx="5" ry="5" width="25" height="10" />
    <circle cx="320" cy="38" r="38" />
    <rect x="307" y="83" rx="5" ry="5" width="25" height="10" />
    <circle cx="410" cy="38" r="38" />
    <rect x="398" y="83" rx="5" ry="5" width="25" height="10" />
  </ContentLoader>
)

Hola Devs! le agregué un skeleton load a las categorias:

JSX

  const skeletonLoadCategories = () => {
    return [1, 2, 3, 4, 5, 6].map(item => (
      <Item key={item + 'categoriesSkeleton'}>
        <SkeletonItem />
      </Item>
    ))
  }

  const renderList = (fixed) => (
    <List fixed={fixed}>
      {
        loading
          ? skeletonLoadCategories()
          : categories.map(category => <Item key={category.id}><Category {...category} /></Item>)
      }
    </List>
  )

styles.js

export const SkeletonItem = styled.div`
  ${twinkle};
  width: 70px;
  height: 70px;
  border-radius: 50%;
  background: #ddd;
`

Y aquí esta el code de la animación:

const twinkleKeyFrames = keyframes`
  0% {
    filter: blur(1px);
    opacity: .5;
  }
  50% {
    filter: blur(1px);
    opacity: 1;
  }
  100% {
    filter: blur(1px);
    opacity: .5;
  }
`

export const twinkle = ({ time = '1.5s', type = 'linear' } = {}) => css`animation: ${time} ${twinkleKeyFrames} ${type} infinite;`

Les recomiendo usar este paquete de NPM; react-loader-spinner
están buenísimos!

Si quieren simular una “Carga lenta” del sitio web, se puede configurar desde el navegador:

  1. Click derecho -> Inspeccionar
  2. Click en Network / Red
  3. Abrir lista desplegable (Por defecto esta seleccionado No throttling)
  4. Seleccionar Slow 3G o offline

De esta manera el navegador se comportará como si tuviera una conexión lenta (o incluso nula) a internet lo que hara que el fetch tome más tiempo en completarse, o que no se haga y el loading quede activo.

Una forma de hacer el loading, en mi caso utilice como base un loader de aquí: https://loading.io/css/
El código aquí

Reto:

Loading/index.js

import React from 'react';

import { ContainerCategorySkeleton, CategoryImage, CategoryTitle } from'./styles';

export const Loading = props => {
    console.log(props)
    return (
        <ContainerCategorySkeleton>
            <CategoryImage light={props.light} />
            <CategoryTitle light={props.light} />
        </ContainerCategorySkeleton>
    )
}

Loading/styles.js

import styled, {css} from 'styled-components';
import { skeletonStyle } from '../../Styles/skeleton';

export const ContainerCategorySkeleton = styled.div`
    display: flex;
    flex-direction: column;
    align-items: center;
`;

export const CategoryImage = styled.div`
    width: 75px;
    height: 75px;
    border-radius: 50%;
    ${
        props => css`
            ${skeletonStyle(props.light)}
        `
    }
`;

export const CategoryTitle = styled.div`
    width: 15px;
    height: 15px;
    margin-top: 8px;
    margin-bottom: 8px;
    border-radius: 50%;
    ${
        props => css`
            ${skeletonStyle(props.light)}
        `
    }
`;

Styles/skeleton.js

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

const skeletonBackground = (light) => (
    css`
        background: ${ !light
            ? css`linear-gradient(-90deg, #C1C1C1 0%, #F8F8F8 50%, #C1C1C1 100%)`
            : css`linear-gradient(-90deg, #F0F0F0 0%, #F8F8F8 50%, #F0F0F0 100%)`};
            background-size: 400% 400%;
            animation: ${skeletonLoading} 1.2s ease-in-out infinite;
    `
)

const skeletonLoading = keyframes`
    from {
        background-position: 0% 0%;
    }
    to {
        background-position: -135% 0%;
    }
`;

export const skeletonStyle = (light = true) => skeletonBackground(light);

ListOfCategories/index.js

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

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

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

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

    return { categories, loading }
}

export const ListOfCategories = () => {
    const {categories, loading } = useCategoriesData();
    
    const [showFixed, setShowFixed] = useState(false);

    //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 fixed={fixed}>
            {
                loading 
                ? 
                <Item key = 'loading'>
                    <Loading />
                </Item>
                : 
                categories.map(category => 
                    <Item key = {category.id}>
                        <Category {...category} />
                    </Item>
                )
            }
        </List>
    )

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

Alguien más redujo el código creando otro custom hook para el scroll, se ve mas limpio y para el loader use un icon y le di un rotate infinito ^^

Estoy realizando todo el proyecto con typescript, les dejo una pequeña parte del código, adicional los spinners los realice con material-ui

import { CircularProgress } from "@mui/material";
import React, { FC, useState, useEffect } from "react";
import { Category } from "../Category";

import { List, Item } from "./styles";
const useCategoriesDate = () => {
  const [loading, setLoading] = useState(true);
  const [categories, setCategories] = useState<categories[]>([]);
  useEffect(() => {
    const query = async () => {
      try {
        const respuesta = await fetch("https://petgram-mikex.vercel.app/categories");
        setLoading(false);
        return setCategories(await respuesta.json());
      } catch (error) {
        console.error(error);
      }
    };
    query();
  }, []);

  return { categories, loading };
};

interface categories {
  id: number;
  name: string;
  emoji: string;
  cover: string;
  path: string;
}

export const ListCategories: FC = () => {
  const { categories, loading } = useCategoriesDate();
  const [showFixed, setShowFixed] = useState(true);

  useEffect(() => {
    const onScroll = (e: Event) => {
      const newShowFixed = window.scrollY < 200;
      showFixed !== newShowFixed && setShowFixed(newShowFixed);
    };

    document.addEventListener("scroll", onScroll);
    return () => {
      window.removeEventListener("scroll", onScroll);
    };
  }, [showFixed]);

  const renderList = (boolVar: boolean) => (
    <List className={(boolVar && ((showFixed && "fixed none") || "fixed")) || ""}>
      {(loading &&
        [1, 2, 3, 4, 5].map((i) => (
          <CircularProgress 
            style={{ width: "65px", height: "65px", margin: ".8em" }} 
            variant="indeterminate" 
            color="secondary" key={i} />
        ))) ||
        categories.map((category) => (
          <Item key={category.id}>
            <Category {...category} />
          </Item>
        ))}
    </List>
  );

  return (
    <React.Fragment>
      {renderList(false)}
      {renderList(true)}
    </React.Fragment>
  );
};

Hola Miguel! Es cierto muy útil utilizar las props de styled-component, pero si necesitamos una animación de entrada y otra se salida, no sería mejor usar clases de css? Muchas gracias!

Aquí el mío, le puse un retardo para que se notara.

Genial

Está genial, resulta más práctico que con clases usar sólo funciones y hooks

Super Claseee!!!

Bastante contento con el curso hasta ahora.

Yo le he puesto un pequeño Spinner, cogiendo el código desde esta página https://loading.io/css/

Aquí el resultado por ahora:

Excelente el ejemplo de los cutom hooks , ya queda un poco mas claro cuando poder usarlos

Para agregar nprogress yo cree otro useEffect donde pregunto por el estado y uso nprogress.start() y done()
como no se trata de renderizar un componente como tal sino de usar la libreria lo hice asi.

Si lo estoy usando de manera incorrecta por favor alguien que me aviseee.

Mi código con comentarios:

import React, { Fragment, useEffect, useState } from 'react'
import { Category } from '../Category'

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

// Costum Hook
function useCategoriesData () {
  // Creando estados iniciales.
  const [categories, setCategories] = useState([])
  const [loading, setLoading] = useState(false)

  useEffect(function () {
    setLoading(true)
    window.fetch('http://localhost:3000/categories')
      .then(res => res.json())
      .then(response => {
        setCategories(response)
        setLoading(false)
      })
  }, [])


  return { categories, loading }
}

export const ListOfCategories = () => {

  // Obtener los valores de los estados en en el custom hook
  const { categories, loading } = useCategoriesData()
  
  const [showFixed, setShowFixed] = useState(false)


  useEffect(function () {
    const onScroll = e => {
      const newShowFixed = window.scrollY > 200
      showFixed !== newShowFixed && setShowFixed(newShowFixed)
    }

    document.addEventListener('scroll', onScroll)

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

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

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

Yo use la libreria skeleton
react-loading-skeleton

Este es mi código con un icono simple de react-loading
Tienen que descargar la libreria con npm install react-loading para que les funciones en . También pueden cambiar el tipo de icono y su color!

import React, { Fragment, useEffect, useState } from 'react'
import { Category } from '../Category'
import { List, Items } from './styles'
import ReactLoading from 'react-loading';


const CategoriesData = () => {
  const [categories, setCategories] = useState([])
  const [loading, setLoading] = useState(false)

  useEffect(function () {
    setLoading(true)
    window.fetch('https://petgram-server-rdct.vercel.app/categories')
      .then(res => res.json())
      .then(response => {
        setCategories(response)
        setLoading(false)
      })
      
  }, [])
  return {categories, loading }
}




export const ListOfCategories = () => {
 
  const [showFixed, setShowFixed] = useState(false)
  const {categories, loading} = CategoriesData()
 

  useEffect(() => {
    const onScroll = e => {
      const newShowFixed = window.scrollY > 200
      showFixed !== newShowFixed && setShowFixed(newShowFixed)
    }

    document.addEventListener('scroll', onScroll)

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



  const renderList = (fixed) => (
    <List fixed= {fixed}>
      {
      loading ? (<ReactLoading type= 'spokes' color= "#000000"/>) :
       categories.map(category => <Items key={category.id}><Category {...category} /></Items>)
      }
    </List>
  )



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

Utilice la pagina https://loading.io/css/
modifique un poro el código y lo implemente en un componente

import React from 'react';
import { Div } from './styles';

export const LoadingSpinner = () => (
	<Div>
		<div> </div>
		<div> </div>
		<div> </div>
	</Div>
);
import styled, { keyframes } from 'styled-components';

const loadingKeyFrames = keyframes`
	from {
	    transform: rotate(0deg);
	  }
	  to {
	    transform: rotate(360deg);
	  }
`;

export const Div = styled.div`

	display: inline-block;
  position: relative;
  width: 80px;
  height: 80px;
	animation: ${loadingKeyFrames} 2s linear infinite;

	& div {
		box-sizing: border-box;
	  display: block;
	  position: absolute;
	  width: 64px;
	  height: 64px;
	  margin: 8px;
	  border: 8px solid #f3a474;
	  border-radius: 50%;
	  animation: lds-ring 1.2s cubic-bezier(0.5, 0, 0.5, 1) infinite;
	  border-color: #f3a474 transparent transparent transparent;
	}

	& div:nth-child(1) {
		animation: ${loadingKeyFrames} 2.1s linear infinite;
	}

	& div:nth-child(2) {
		animation: ${loadingKeyFrames} 2.2s linear infinite;
	}

	& div:nth-child(3) {
		animation: ${loadingKeyFrames} 2.3s linear infinite;
	}
`;

Mi solución usando https://tobiasahlin.com/spinkit/

src\components\Loader\index.js

import React from 'react'
import { Spinner } from './Styles'

export const Loader = () => {
  return (
    <Spinner />
  )
}

src\components\Loader\Styles.js

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

const skRotateplane = keyframes`
  0% { -webkit-transform: perspective(120px) }
  50% { -webkit-transform: perspective(120px) rotateY(180deg) }
  100% { -webkit-transform: perspective(120px) rotateY(180deg)  rotateX(180deg) }
`

export const Spinner = styled.div`
  width: 40px;
  height: 40px;
  background-color: #333;
  margin: 100px auto;
  animation: ${skRotateplane} 1.2s infinite ease-in-out;
`

Loading con skeleton

Bueno lo que hice, para resolver esto es crear en la carpeta styles un archivo CategorySkeleton

Mi idea es, mas adelante cuando se haga lo mismo con el componente de listOfPhotoCategory retocarlo y que sea cuadrado y asi tener un mismo archivo

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

const skeletonBackground = () => (
  css`animation: ${skeletonLoading} 1.6s ease-in-out infinite;`
)

const skeletonLoading = keyframes`
  from {
    background-position: 0% 0%;
    background: linear-gradient(-90deg, #C1C1C1 0%, #F8F8F8 50%, #C1C1C1 100%);
    background-size: 400% 400%;
  }
  to {
    background-position: -135% 0%;
    background: linear-gradient(-90deg, #F0F0F0 0%, #F8F8F8 50%, #F0F0F0 100%);
    background-size: 400% 400%;
  }
`

export const ListSkeleton = styled.div`
  margin-bottom: 20px;
`
export const ListSkeletonPhoto = styled.div`
  ${skeletonBackground()}
  background: #909090;
  height: 10vh;
  width: 5vw;
  border-radius: 80%;
  margin-bottom: 10px;
`
export const ListSkeletonBody = styled.div`
  ${skeletonBackground()}
  background: #909090;
  height: 20px;
  width: 30px;
  margin: auto;
`

Index>Category

 const renderList = (fixed) => (
    <List fixed={fixed}>
      {
        loading
          ? [1, 2, 3, 4, 5, 6].map((key) => {
            return (
              <Item key={key}>
                <ListSkeleton>
                  <ListSkeletonPhoto />
                  <ListSkeletonBody />
                </ListSkeleton>
              </Item>
            )
          })
          : (
            categories.map((category, key) =>
              <Item key={key}>
                <Category
                  {...category}
                />
              </Item>
            )
          )
      }
    </List>
  )

Es fascinante!!!

Yo muestro contenedores vacios con shadows internos y cuando cargan las imágenes pues se muestran con un fade in

yo use react-loader-spinner, este es mi resultado:

Osea que así de sencillo es crear tu propio hook. Solo es una función que devuelve un valor y ya xD Creí que iba a ser mucho más difícil.

Excelente el conjunto de buenas prácticas, creo que no voy tan perdido

Para el loading use el componente “react-loader-spinner” es muy facil de instalar y tiene muchas opciones de animacion para usar.

https://www.npmjs.com/package/react-loader-spinner

b

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

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

usé el código de la animación para cuando la categoría no ha cargado de iLuisCastillo

Muy bueno 😃