Mi solución basada en las últimas actualizaciones de la documentación
Introducción al curso avanzado de React
Qué necesitas para este curso y qué aprenderás sobre React.js
Proyecto y tecnologías que usaremos
Preparando el entorno de desarrollo
Clonando el repositorio e instalando Webpack
Instalación de React y Babel
Zeit es ahora Vercel
Linter, extensiones y deploy con Now
Creando la interfaz con styled-components
¿Qué es CSS-in-JS?
Creando nuestro primer componente: Category
Creando ListOfCategories y estilos globales
Usar información real de las categorías
Creando PhotoCard y usando react-icon
SVGR: de SVG a componente de ReactJS
Creando animaciones con keyframes
Hooks
¿Qué son los Hooks?
useEffect: limpiando eventos
useCategoriesData
Usando Intersection Observer
Uso de polyfill de Intersection Observer e imports dinámicos
Usando el localStorage para guardar los likes
Custom Hooks: useNearScreen y useLocalStorage
GraphQL y React Apollo
¿Qué es GraphQL y React Apollo? Inicializando React Apollo Client y primer HoC
Parámetros para un query con GraphQL
Usar render Props para recuperar una foto
Refactorizando y usando variables de loading y error
Usando las mutaciones con los likes
Reach Router
¿Qué es Reach Router? Creando la ruta Home
Usando Link para evitar recargar la página
Creando la página Detail
Agregando un NavBar a nuestra app
Estilando las páginas activas
Rutas protegidas
Gestión del usuario
Introducción a React.Context
Creación del componente UserForm; y Hook useInputValue
Estilando el formulario
Mutaciones para registro
Controlar estado de carga y error al registrar un usuario
Mutaciones para iniciar sesión
Persistiendo datos en Session Storage
Hacer like como usuario registrado
Mostrar favoritos y solucionar fetch policy
Cerrar sesión
Mejores prácticas, SEO y recomendaciones
Últimos retoques a las rutas de nuestra aplicación
React Helmet
Midiendo el performance de nuestra app y usando React.memo()
React.lazy() y componente Suspense
Usando PropTypes para validar las props
PWA: generando el manifest
PWA: soporte offline
Testing con Cypress
Conclusiones
¡Felicidades!
Aún no tienes acceso a esta clase
Crea una cuenta y continúa viendo este curso
Un JSON Web Token (JWT) es un estándar abierto para crear tokens y asegurar que el envío de datos es confiable y seguro. Van a ser muy útiles para implementar la lógica de los likes pues solamente los usuarios autentificados podrán dar like.
Un JWT se conforma de 3 partes:
Para utilizar nuestro JWT necesitamos añadirlo al header authorization
de las peticiones HTTP que hagamos con el texto Bearer [token]
.
Aportes 30
Preguntas 7
Mi solución basada en las últimas actualizaciones de la documentación
https://jwt.io/ Acá puede decodificar los JWT y ver que información están enviando.
Este es el codigo que yo utilice ya que ando usando @apollo/client
import React from 'react'
import { ApolloClient, ApolloProvider as Aprovider, InMemoryCache, createHttpLink } from '@apollo/client'
import { setContext } from '@apollo/client/link/context'
const httpLink = createHttpLink({
uri: 'https://petgram-server-anthony-3vrjckvsb.vercel.app/graphql'
})
const authLink = setContext((_, { headers }) => {
const token = window.sessionStorage.getItem('token')
return {
headers: {
...headers,
authorization: token ? `Bearer ${token}` : ''
}
}
})
const client = new ApolloClient({
link: authLink.concat(httpLink),
cache: new InMemoryCache()
})
export const ApolloProvider = ({ children }) => (
<Aprovider client={client}>
{children}
</Aprovider>
)
Lo separe al igual que el Context para que no se vieran con tanta informacion el index, me guie de la documentacion de apollo https://www.apollographql.com/docs/react/networking/authentication/
La cual muestra un ejemplo, lo que pude entender es que usaremos las variables setContext y createHttpLink para luego concatenarlas en el ApolloClient y asi poder interceptar las peticiones que se hagan para interactuar segun si se tiene el token o no
Les comparto el codigo de @Félix Anderson, al cual solo he agregado el window.sessionStorage en lugar de sessionStorage y en un formato que puedan copiar, gracuas Félix!
import React from 'react'
import ReactDOM from 'react-dom'
import { App } from './App'
import { ApolloClient, InMemoryCache, ApolloProvider, ApolloLink, from, HttpLink } from '@apollo/client'
import Context from './Context'
import { onError } from '@apollo/client/link/error'
const authMiddleware = new ApolloLink((operation, forward) => {
const token = window.sessionStorage.getItem('token')
if (token) {
operation.setContext({
headers: {
authorization: `Bearer ${token}`
}
})
}
return forward(operation)
})
const errorMiddleware = onError(({ networkError }) => {
if (networkError && networkError.result.code === 'invalid_token') {
window.sessionStorage.removeItem('token')
window.location = '/user'
}
})
const client = new ApolloClient({
cache: new InMemoryCache(),
link: from([
errorMiddleware,
authMiddleware,
new HttpLink({
uri: 'https://petgram-server-mcvictor.mcvictormurillo.vercel.app/graphql'
})
])
})
ReactDOM.render(
<Context.Provider value={{ isAuth: false }}>
<ApolloProvider client={client}>
<App />
</ApolloProvider>
</Context.Provider>
, document.getElementById('app'))
SI por alguna razon iniciaron a usar apollo con base a este comentario
ALTO AQUI
En su codigo usaron:
import { ApolloClient } from'apollo-boost'
Y deben de hacerlo como:
import ApolloClient from 'apollo-boost'
De lo contrario tendrán que añadir mas código y hacer mucha configuración perdiendo el poder de ApolloBoost
El código final de la clase queda asi:
import ApolloClient from'apollo-boost'
const client = new ApolloClient({
uri: 'https://petgram-server.miguelseguramx.now.sh/graphql',
request: operation => {
const token = window.sessionStorage.getItem('token')
const authorization = token ? `Bearer ${token}` : ''
operation.setContext({
headers: {
authorization
}
})
},
onError: error => {
const { networkError } = error
if (networkError && networkError.result.code === 'invalid_token') {
window.sessionStorage.removeItem('token')
window.location.href = '/'
}
}
})
Les dejo mi código porseacaso, lo hice con guiandome de otros aportes y con esta documentación https://www.apollographql.com/docs/react/networking/advanced-http-networking/#customizing-request-logic
src > index.js
src > components > PhotoCard > index.js
Con que curso puedo crear un API de backend como esta ❤️!
Mi porte para que puedan tener la autenticación junto con el manejo de errores🤗🤗 con @apollo/client :
A mi me pasa que el servidor (API en GraphQL), recuerda el usuario registrado solo por un momento, luego arroja que no existe
import React from 'react'
import ReactDOM from 'react-dom'
import { App } from './App.js'
import { ApolloClient, InMemoryCache, ApolloProvider, createHttpLink, from } from '@apollo/client'
import Context from './Context'
import { setContext } from '@apollo/client/link/context'
import { onError } from '@apollo/client/link/error'
const httpLink = createHttpLink({
uri: 'https://petgram-server-eli.elizabethriver.vercel.app/graphql'
})
const authLink = setContext((_, { headers }) => {
// get the authentication token from local storage if it exists
const token = window.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 = '/user'
}
})
})
ReactDOM.render(
<Context.Provider>
<ApolloProvider client={client}>
<App />
</ApolloProvider>
</Context.Provider>
, document.getElementById('app')
)
si llegaron hasta este punto y quieren algo de documentacion actual y ahorarse un par de horitas https://www.apollographql.com/docs/react/networking/advanced-http-networking/#customizing-request-logic
Así me quedó usando ladocumentación actualizada de @apollo/client:
import React from "react";
import ReactDOM from "react-dom";
import {
ApolloClient,
ApolloProvider,
createHttpLink,
InMemoryCache,
} from "@apollo/client";
import { setContext } from "@apollo/client/link/context";
import { App } from "./App";
import Context from "./Context";
const httpLink = createHttpLink({
uri: "localhost:3500/graphql",
});
const authLink = setContext((_, { headers }) => {
const token = sessionStorage.getItem("token");
return {
headers: {
...headers,
authorization: token ? `Bearer ${token}` : "",
},
};
});
const client = new ApolloClient({
link: authLink.concat(httpLink),
cache: new InMemoryCache(),
});
ReactDOM.render(
<Context.Provider value={{ isAuth: true }}>
<ApolloProvider client={client}>
<App />
</ApolloProvider>
</Context.Provider>,
document.getElementById("app")
);
Un JWT se conforma de 3 partes:
Header: Es un objeto que define qué algoritmo y tipo tiene el token.
Payload: La información que almacenamos en el token.
Verify Signature: Una encriptación del header más el payload más tu llave secreta.
Para utilizar nuestro JWT necesitamos añadirlo al header authorization de las peticiones HTTP que hagamos con el texto Bearer [token].
Esta es mi solucion para configurar el cliente de ApolloClient
Al parecer apollo cambio la manera de escribir en el setContext, ese metodo ya no me funciona, si podrian ayudar porque nose como se estara implementando ahora.
Tuve problemas con mi aplicación ya que en el formulario de iniciar sesión y en el formulario de crear cuenta, estaba usando un hook llamado “useLocalStorage”.
.
Para guardar en localStorage y leer información de localStorage, la información debe ser parseada a JSON y para leerla debe ser “stringifyteada” de JSON a String, aprendí en esta clase que el sessionStorage no necesita este proceso (creo?).
.
¿Qué estaba pasando?
Podía crear la cuenta de mi usuario, y podía iniciar sesión. Pero, al momento de dar like con mi usuario, la aplicación me indicaba error, que el token era invalido. Tras revisar varias veces mi código, se me ocurrió visitar las herramientas del desarrollador de Chrome y en el header estaba la respuesta.
.
La solicitud “authorization” estaba de la siguiente manera: Bearer "40d83usdje0923....."
cuando, el authorization debía viajar de la siguiente manera: Bearer 40d83usdje0923.....
… ¿Ves cómo mi token era un String? Ese era el problema.
.
¿Cómo lo solucioné?
Simplemente modifiqué mi hook de useLocalStorage para pasarlo a useSessionStorage, adicionalmente tuve que cambiar su implementación en el contexto de la app para acomodar su nombre unicamnete.
Ahora el hook, me quedó de la siguiente manera:
.
hooks/useSessionStorage.js
import { useState } from 'react'
function useSessionStorage(key, initValue) {
const [storedValue, setValue] = useState(() => {
try {
const item = window.sessionStorage.getItem(key)
return item !== null ? item : initValue
} catch (error) {
console.error(error)
return initValue
}
})
const setSessionStorage = value => {
try {
window.sessionStorage.setItem(key, value)
setValue(value)
} catch (error) {
console.error(error)
}
}
return [storedValue, setSessionStorage]
}
export default useSessionStorage
asi me quedo
import React from 'react'
import ReactDOM from 'react-dom'
import { App } from './App'
import { ApolloProvider, ApolloClient, InMemoryCache, createHttpLink } from '@apollo/client'
import { setContext } from '@apollo/client/link/context'
import Context from './Context'
import { onError } from '@apollo/client/link/error'
const httpLink = createHttpLink ({
uri: 'https://api-nine-gamma.vercel.app/graphql'
});
const authLink = setContext ((_, { headers }) => { // get the authentication token from local storage if it exists
const token = window.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 = '/'
}
}
)
})
ReactDOM.render(
<Context.Provider>
<ApolloProvider client={client}>
<App />
</ApolloProvider>
</Context.Provider>,
document.getElementById('app')
)
// https://www.apollographql.com/docs/react/networking/authentication/
// https://www.apollographql.com/docs/react/networking/advanced-http-networking/#customizing-request-logic
Esto esta funcionando perfectamente en Mayo 2021
Tuve que poner Bearer en mayúsculas la primera letra, no me funcionaba en minúsculas, salía usuario no encontrado.
import ApolloClient from "apollo-boost";
import { ApolloProvider } from "@apollo/react-hooks";
const client = new ApolloClient({
uri: "https://petgram-ianpedraza-hs26ht9s0-ianpedraza.vercel.app/graphql",
request: (operation) => {
const token = window.sessionStorage.getItem("token");
const authorization = token ? `Bearer ${token}` : "";
operation.setContext({
headers: {
authorization,
},
});
},
onError: (error) => {
const { networkError } = error;
if (networkError && networkError.result.code === "invalid_token") {
window.sessionStorage.removeItem("token");
window.location.href = "/";
}
},
});
Me salio error de Apollo Client que no estaba registrado, lo que pude notar era que al registrar un nuevo usuario me logeaba de una, esto hacia que el token solo estuviera en registrar y no iniciar sesión.
Lo pude solucionar registrando por GraphQL e iniciando sesión por la aplicación y no me volvio a generar ese error
Todo un manejo de login controlado y estable, está lindo
Hola Devs:
-Les recomiendo si desean leer mas sobre JWT (JSON Web Tokens), les dejo una muy buena lectura: JWT
Recuerden, #NuncaParesDeAprender 💚
Encontré esta informacion sobre los JWT vale la pena leerla
mi código de index usando @apollo/client
import React from 'react'
import ReactDOM from 'react-dom'
// Graph QL Imports
import { ApolloClient, InMemoryCache, createHttpLink } from '@apollo/client'
import { ApolloProvider } from '@apollo/react-hooks'
import { setContext } from '@apollo/client/link/context'
import App from './App'
// Global State
import Context from './Context'
const httpLink = new createHttpLink({
uri: 'https://pixtagram-server.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(),
})
ReactDOM.render(
<Context.Provider>
<ApolloProvider client={client}>
<App />
</ApolloProvider>
</Context.Provider>,
document.getElementById('app')
)
Tomando el reto se agrega solo bloqueo a feature photoCardsDetails
export const App = () => {
return (
<div>
<GlobalStyles />
<Logo />
<Router>
<Home path='/' />
<Home path='/pet/:categoryId' />
</Router>
<Context.Consumer>
{
({ isAuth }) =>
isAuth
? (
<Router>
<Detail path='/detail/:detailId' />
<Favorite path='/favorite' />
<User path='/user' />
</Router>
)
: (
<Router>
<NotRegisterUser path='/detail/:detailId' />
<NotRegisterUser path='/favorite' />
<NotRegisterUser path='/user' />
</Router>
)
}
</Context.Consumer>
<NavBar />
</div>
)
}
configuracion apollo
Wow!
Muy interesante el tema de JWT
Ahora me dan ganas de estudiar backend jaja
n
Hola, les dejo el link de mi repositorio, hecho hasta ahora en Nextjs:
https://github.com/danyel117/petgram-platzi/tree/login
genial!!
¿Quieres ver más aportes, preguntas y respuestas de la comunidad? Crea una cuenta o inicia sesión.