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

Cerrar sesi贸n

41/50
Recursos

Aportes 14

Preguntas 2

Ordenar por:

驴Quieres ver m谩s aportes, preguntas y respuestas de la comunidad?

o inicia sesi贸n.

A mi me pasaba que cuando me logueaba o cerraba sesi贸n, no se actualizaban los datos de los likes, es decir, apollo sigue usando la cache vieja, osea que al cerrar sesi贸n segu铆a viando los likes viejos, o lo mismo si cambiaba de usuario.

La soluci贸n m谩s sencilla que encontr茅 es exportar el objeto client de index.js, e importarlo en Context.js, para as铆 limpiar la cache cuando cambia el estado de autenticaci贸n, ya sea si se inicia o se cierra una sesi贸n.

Tambi茅n hay que borrar el estado de Apollo client al hacer logout:

  • Context.js
import { useApolloClient } from "@apollo/client";
const client = useApolloClient();
removeAuth: () => {
      setIsAuth(false);
      window.sessionStorage.removeItem("token");
      client.resetStore();
    },

Para que funcione asegurense que el Context.Provider est茅 dentro de Provider de Apollo:

  • index.js
ReactDOM.render(
  <ApolloProvider client={client}>
    <Context.Provider value={{ isAuth: true }}>
      <App />
    </Context.Provider>
  </ApolloProvider>,
  document.getElementById("app")
);

El ciclo completo de una aplicaci贸n, de verdad me sorprendi贸 este curso

Hola a todos, la fecha es 05/04/2022
Les dejo el codigo que utilice:

import React from 'react'
import ReactDOM from 'react-dom'
import { ApolloClient, InMemoryCache, ApolloProvider, createHttpLink } from '@apollo/client'
import { setContext } from '@apollo/client/link/context'
import { onError } from '@apollo/client/link/error'
import { AppProvider } from './context/AppContext'
import {App} from './App'

const httpLink = createHttpLink({
  uri: "https://petgram-server-max-seven.vercel.app/graphql",
})

const authLink = setContext((_, { headers }) => {
  // get the authentication token from local storage if it exists
  const token = sessionStorage.getItem('token')
  // return the headers to the context so httpLink can read them
  return {
    headers: {
      ...headers,
      authorization: token ? `Bearer ${token}` : ''
    }
  }
})

const client = new ApolloClient({
  link: authLink.concat(httpLink),
  cache: new InMemoryCache(),
  onError: onError(({ networkError }) => {
    if (networkError && networkError.result.code === 'invalid_token') {
      window.sessionStorage.removeItem('token')
      window.location.href = '/user'
    }
  })
})

ReactDOM.render(
  <ApolloProvider client={client} >
    <AppProvider>
      <App />
    </AppProvider>
  </ApolloProvider>

, document.getElementById('app'))

import React, { createContext, useState } from 'react'
import { useApolloClient } from '@apollo/client'

const AppContext = createContext(null);

const AppProvider = (props) =>{
  const [isAuth, setIsAuth ] = useState(()=>{
    return sessionStorage.getItem('token')
  });
  const client = useApolloClient()


  const activateAuth = (token) => {
    setIsAuth(true)
    sessionStorage.setItem('token', token)
    
  }

  const removeAuth = ()=>{
    setIsAuth(false)
    sessionStorage.removeItem('token')
    client.resetStore()
  }

  const value = {
    isAuth,
    activateAuth,
    removeAuth
  }

  return (
    <AppContext.Provider value ={value}>
      {props.children}
    </AppContext.Provider>
  )
}


export { AppContext, AppProvider };

import React from 'react'
import { Button } from './styles'

export const SubmitButton = ({onClick, text, disabled = false})=>{
  return (
    <Button disabled={disabled} onClick={onClick} >{text}</Button> 
  )
}

import styled from "styled-components";

export const Button = styled.button`
background: #8d00ff;
border-radius: 3px;
color: #fff;
height: 32px;
display: block;
width: 100%;
text-align: center;
&[disabled]{
  opacity: 0.3;
}
`
import React from 'react'
import { useInputValue } from '../../hooks/useInputValue'
import { SubmitButton } from '../SubmitButton'
import { Form, Input, Title, Error, Spinner } from './styles'

const UseForm = ({ onSubmit, title, error, disabled })=>{
  const email     = useInputValue('')
  const password  = useInputValue('')
  const handlerSubmit = (event)=>{
    event.preventDefault()
    onSubmit({email: email.value, password: password.value})
  }
  return (
    <>
      <Form onSubmit={handlerSubmit}>
        <Title>{title}</Title>
        <Input disabled={disabled} placeholder='email' {...email} />
        <Input disabled={disabled} placeholder='password' type='password' {...password} /> 
        <SubmitButton text={title} disabled={disabled} />
      </Form>
      {disabled && <Spinner></Spinner>}
      {error && <Error>{error}</Error>}
    </>
    
  )
}

export { UseForm }

no se si a alguien mas le pasa pero no me funciona el useContext. desde la pagina de notRegisteredUser.js he tenido que usar el metodo del Context.Consumer sino me sale un error de que context no esta definido鈥 a alguien mas le pasa?

Faltar铆a solo la validaci贸n de los datos que pasamos en los formularios ya que por ejemplo hasta los momentos puedo registrar un usuario pasando datos vac铆os de user y password, pero supongo que es para otro curso o aqu铆 mismo mas adelante

No me parece que la manera de crear componentes por carpetas con sus estilos dentro sea la mejor manera de reutilizar ciertas caracteristicas

Yo utilice redux toolkit, aqui dejo mi codigo para quien hiciera lo mismo:

import { createSlice } from "@reduxjs/toolkit"

const initialState = {
  isAuth : sessionStorage.getItem('token')
}

const authSlice = createSlice({
  name: 'auth',
  initialState,
  reducers: {
    activateAuth: (state, action) => {
      sessionStorage.setItem('token', action.payload)
      state.isAuth = action.payload;
    },
    removeAuth: (state, action) =>{
      sessionStorage.removeItem('token');
      state.isAuth = false;
    }
  }
})

export const { activateAuth, removeAuth } = authSlice.actions;

export const authReducer = authSlice.reducer;

import React from 'react'
import { Button } from './styles'

export const SubmitButton = ({onClick, text, disabled = false})=>{
  return (
    <Button disabled={disabled} onClick={onClick} >{text}</Button> 
  )
}

import styled from "styled-components";

export const Button = styled.button`
background: #8d00ff;
border-radius: 3px;
color: #fff;
height: 32px;
display: block;
width: 100%;
text-align: center;
&[disabled]{
  opacity: 0.3;
}
`

import styled from "styled-components";

export const Form = styled.form`
  padding: 16px 0;
`

export const Input = styled.input`
  border: 1px solid #ccc;
  border-radius: 3px;
  margin-bottom: 8px;
  padding: 8px 4px;
  display: block;
  width: 100%;
  &[disabled]{
    opacity: 0.3;
  }
`

export const Title = styled.h2`
  font-size: 16px;
  font-weight: 500;
  padding: 8px 0;
`

export const Error = styled.span`
  font-size: 14px;
  color: red;
`
export const Spinner = styled.div`
  @keyframes spin {
    0% {
      transform: rotate(0deg);
    }
  
    100% {
      transform: rotate(360deg);
    }
  
  }
  border: 4px solid rgba(0, 0, 0, 0.1);
  width: 36px;
  height: 36px;
  border-radius: 50%;
  border-left-color: #09f;
  margin:auto;
  animation: spin 1s ease infinite;
`

import React from 'react'
import { useInputValue } from '../../hooks/useInputValue'
import { SubmitButton } from '../SubmitButton';
import { Error, Form, Input, Spinner, Title } from './styles'

const UserForm = ({ onSubmit, title, disabled, error })=>{
  const email     = useInputValue('');
  const password  = useInputValue('');
  const handlerSubtmit = (event) =>{
    event.preventDefault();
    onSubmit({email: email.value, password: password.value});
  };
  
  return (
    <>
      <Title>{title}</Title>
      <Form onSubmit={handlerSubtmit}>
        <Input disabled={disabled} placeholder='email' {...email} />
        <Input disabled={disabled} placeholder='password' type='password' {...password} />
        <SubmitButton disabled={disabled} text={title} />
      </Form>
      {disabled && <Spinner />}
      {error && <Error>{error}</Error>}
    </>
  )
}

export { UserForm }

import React from "react"
import { SubmitButton } from "../components/SubmitButton"
import { useDispatch } from 'react-redux'
import { removeAuth } from "../store/authReducer";

export const User = ()=>{
  const dispatch = useDispatch();
  const handlerLoguot = ()=>{
    dispatch(removeAuth());
  }

  return (
    <>
      <SubmitButton text={'Cerrar sesin'} onClick={handlerLoguot} />
    </>
  )
}

Lord

As铆 solucion茅 el problema del hook de context:

import React from 'react';
import Context from '../Context';

export const User = () => {
	return (
		<Context.Consumer>
			{
				({ removeAuth }) => {
					return (
						<>
							<h1>User</h1>

							<button onClick={removeAuth}>Logout</button>
						</>
					)
				}
			}
		</Context.Consumer>
	);
};

No se si alguien mas lo nota, pero al momento de iniciar sesion y dirigirse al home no muestra las imagenes que tienen me gusta, hay que recargar la pagina para que aparezcan, como se haria para solucionar este problema? en este caso no se puede utilizar el fetchPolicy porque los likes se guardan con mutaciones

Si alguno tiene problemas con el context les dejo el que yo hice para que puedan solucinarlo.

import React, { createContext, useState } from 'react'
const Context = createContext()
const { Provider: ContextProvider, Consumer } = Context

const Provider = ({ children }) => {
  const [isAuth, setAuth] = useState(() =>
    window.sessionStorage.getItem('token')
  )
  const value = {
    isAuth,
    activateAuth: token => {
      setAuth(true)
      window.sessionStorage.setItem('token', token)
    },
    removeAuth: () => {
      setAuth(false)
      window.sessionStorage.removeItem('token')
    }
  }

  return <ContextProvider value={value}>{children}</ContextProvider>
}

export { Provider, Consumer, Context }

gracias!!!