¿Podríamos decir que:
- M: withPhotos
- V: ListOfPhotoCardComponent
- C: ListOfPhotoCard
??? es un pequeño MVC del lado del cliente?
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!
No tienes acceso a esta clase
¡Continúa aprendiendo! Únete y comienza a potenciar tu carrera
Aportes 36
Preguntas 6
¿Podríamos decir que:
??? es un pequeño MVC del lado del cliente?
Los react hooks en apollo 😎😎😎
import { useQuery } from 'react-apollo-hooks'
import gql from 'graphql-tag'
const getPhotos = gql`
query getPhotos($categoryId: ID) {
photos(categoryId: $categoryId) {
id
categoryId
src
likes
userId
liked
}
}
`
export const useGetPhotos = categoryId => {
const { loading, data, error } = useQuery(getPhotos, { variables: { categoryId } })
return { loading, data, error }
}
Estoy realizando este ejercicio utilizando hooks, @apollo/client
como cliente y proveedor
En el entrypoint de la app importamos el ApolloClient, ApolloProvider de @apollo/client
/index.js
import React from 'react'
import ReactDOM from 'react-dom'
import { App } from './App'
import { ApolloClient, InMemoryCache, ApolloProvider } from '@apollo/client'
const client = new ApolloClient({
uri: process.env.API_URL,
cache: new InMemoryCache()
})
ReactDOM.render(
<ApolloProvider client={client}>
<App />
</ApolloProvider>
, document.getElementById('app'))
El query lo separé en un custom hook
import { gql, useQuery } from '@apollo/client'
export function useGetPhotos (categoryId) {
const GET_PHOTOS = gql`
query getPhotos($categoryId: ID) {
photos (categoryId: $categoryId){
id
categoryId
src
likes
liked
userId
}
}`
const { loading, error, data } = useQuery(GET_PHOTOS, { variables: { categoryId } })
return { loading, error, data }
}
y ListOfPhotocards.js implementa el custom hook de la forma
import React from 'react'
import { PhotoCard } from '../PhotoCard'
import { useGetPhotos } from '../../hooks/useGetPhotos'
export const ListOfPhotoCards = ({ categoryId }) => {
const { loading, error, data } = useGetPhotos(categoryId)
if (loading) return <p>Loading...</p>
if (error) return <p>error...</p>
return (
<ul>
{data.photos.map((photo) => (
<PhotoCard key={photo.id} id={photo.id} {...photo} />
))}
</ul>
)
}
Mas información en mi repo
Actualización:
Para estas fechas solo se necesita instalar
npm install @apollo/client graphql
el resto de dependencias del video no son necesarias. Luego en tu index.js
import React from 'react'
import ReactDOM from 'react-dom'
import { ApolloProvider, InMemoryCache, ApolloClient } from '@apollo/client'
import { App } from './App'
const client = new ApolloClient({
cache: new InMemoryCache(),
uri: 'https://api-platzi-5vlnixk1i.vercel.app/graphql'
})
ReactDOM.render(
<ApolloProvider client={client}>
<App />
</ApolloProvider>,
document.getElementById('app'))
Todos los componentes se cargan de esa dependencia y en tu consumer, puedes usar esto:
import React from 'react'
import { PhotoCard } from '../PhotoCard'
import { gql, useQuery } from '@apollo/client'
const GET_PHOTOS = gql`
query getPhotos($categoryId: ID) {
photos(categoryId: $categoryId) {
id
categoryId
src
likes
userId
liked
}
}
`
export const ListOfPhotoCards = ({ categoryId }) => {
const { loading, error, data } = useQuery(GET_PHOTOS, {
variables: { categoryId }
})
if (loading) return <span>loading</span>
if (error) return <p>Error</p>
return (
<ul>
{
data.photos.map(photoCard => <PhotoCard key={photoCard.id} {...photoCard} />)
}
</ul>
)
}
ACTUALICEN ESTE CURSOOOOOOOOOOOOOOOO
Bueno, este es el pequeño aporte que puedo hacer, basándome en como estructuró el código un compañero en la clase anterior, para el que no haya implementado el componente padre como hizo el profesor, esta es mi solución, en el listOfPhotoCards, simplemente desestructuran ‘categoryId’ de los props y lo pasan como parámetro en 'variables: categoryId:'
Luego en el App.js ingresan el props con el numero y funciona perfecto.
import React from 'react'
import { PhotoCard } from '../PhotoCard'
import { useQuery } from '@apollo/react-hooks'
import { gql } from 'apollo-boost'
const getPhotos = gql`
query getPhotos($categoryId: ID) {
photos(categoryId: $categoryId) {
id
categoryId
src
likes
userId
liked
}
}
`
export const ListOfPhotoCards = ({ categoryId }) => {
const { loading, error, data } = useQuery(getPhotos, { variables: { categoryId: categoryId } })
if (loading) return <h2>Loading...</h2>
if (error) return <h2>Error!</h2>
return (
<ul>
{data.photos.map(photo => <PhotoCard key={photo.id} id={photo.id} src={photo.src} />)}
</ul>
)
}
El curso venía de maravilla, hasta que llegamos acá, y con las desactualizaciones, perdí al menos dos horas entre errores diferentes.
Es de suma importancia tener éstas cosas en cuenta sino la frustración intentando resolver las cosas es enorme.
Un MVC es un patrón de diseño de software que nos propone separar nuestro código de la siguiente manera:
M: modelo => withPhotos
V: vista => ListOfPhotoCardsComponent
C: controlador => ListOfPhotoCard
 => (
<>
<GlobalStyle />
<Logo />
<ListOfCategories />
<ListOfPhotoCardsContainer />
</>
)
El contenedor tuve que renombrarlo por ListOfPhotoCardsContainer de otra forma genera problema al llamarse igual que componente, el cual, consume el hook para traer los datos y le pasa la data al componente por medio de una prop:
import React from 'react'
import { withPhotos } from '../hoc/withPhotos'
import { ListOfPhotoCards } from '../components/ListOfPhotoCards'
import { useQuery } from '@apollo/client'
export const ListOfPhotoCardsContainer = ({ categoryId }) => {
const { loading, error, data } = useQuery(withPhotos, {
variables: { categoryId }
})
if (error) {
return <h2>Internal Server Error</h2>
}
if (loading) {
return <h2>Loading...</h2>
}
return <ListOfPhotoCards data={data} />
}
El componente ListOfPhotoCards se modifico para que solo muestre la lista de fotos similar a como lo muestra el profesor:
// dependecies
import React from 'react'
// Components
import { PhotoCard } from '../PhotoCard'
export const ListOfPhotoCards = ({ data }) => {
return (
<ul>
{data.photos.map((photo) => (
<PhotoCard key={photo.id} {...photo} />
))}
</ul>
)
}
Trate de mantener la estructura y de momento me está trabajando bien
Implementando el hook de Apollo quedaría así:
const GET_PHOTOS = gql`
query getPhotos($categoryId: ID) {
photos(categoryId: $categoryId) {
id
categoryId
src
likes
userId
liked
}
}
`
const { loading, error, data = { photos: [] } } = useQuery(GET_PHOTOS, {
variables: { categoryId: categoryId }
})
Todo esto esta demasiado desactualizado
Leyendo la documentacion de apollo resolvi tanto esta como la clase anterior Apollo
<code>
const ANIMALS_QUERY = gql`
query getPhotos($categoryId: ID){
photos(categoryId: $categoryId){
id
categoryId
src
likes
userId
liked
}
}
`
export const ListOfPhotoCard = ({ categoryId }) => {
const { data, loading, error } = useQuery(ANIMALS_QUERY, {
variables: { categoryId }
})
if (loading) return <Loading />
if (error) return <pre>{error.message}</pre>
return (
<ul>
{data.photos.map(photo => <PhotoCard key={photo.id} {...photo} />)}
</ul>
)
}
<code>
donde entra redux aqui la verdad no entiendo 😦
Hola, he utilizado el render props ApolloConsumer para disparar una query en una accion(por ejemplo un click) y funciona sin problemas,sin embargo como se implemetaria esto en una la forma de HOC?
Mi solucion usando hooks 👉Click 😉
Para los que usan hooks https://www.apollographql.com/docs/react/api/react/hooks/#usequery
import React from 'react'
import { gql, useQuery } from '@apollo/client'
import { PhotoCard } from '../PhotoCard'
import { List, Item } from './style.js'
const GET_PHOTOS = gql`
query getPhotos($categoryId: ID) {
photos(categoryId: $categoryId) {
id
categoryId
src
likes
userId
liked
}
}
`
export const ListOfPhotoCard = () => {
const { loading, error, data } = useQuery(GET_PHOTOS, {
variables: { categoryId: 1 } //TODO: Enviar dinamicamente
})
if (loading) {
return <h1>Loading</h1>
}
if (error) {
return <h1>{error.message}</h1>
}
return (
<>
<List>
{data.photos.map((photoCard) => (
<Item key={photoCard.id}>
<PhotoCard {...photoCard} />
</Item>
))}
</List>
</>
)
}
Excelente. Gracias!!
Una forma ordenada de trabajar es separar el apollo de nuestro archivo Index de la siguiente manera (espero les sirva)
import React from 'react';
import ReactDOM from 'react-dom';
import { ApolloComponent } from './apollo';
import { App } from './App';
ReactDOM.render(
<ApolloComponent>
<App />
</ApolloComponent>,
document.getElementById('app'));
import React from 'react';
import { ApolloClient, ApolloProvider, InMemoryCache } from '@apollo/client'
const client = new ApolloClient({
uri: "https://petgram-server-max-seven.vercel.app/graphql",
cache: new InMemoryCache(),
});
export const ApolloComponent = ({children})=>{
return (
<ApolloProvider client={client}>
{children}
</ApolloProvider>
)
}
Una aclaracion:
A dia de hoy la metodologia para hacer esta clase es diferente, y lo mas probable es que no hayas utilizado la funcion graphql() para el query withPhotos.
De ser asi, esta funcion NO SE CONSIDERA UN HOC.
Por definicion, un HOC es una funcion que recibe como parametro un componente y retorna otro componente.
You are still able to use either HOCs or render props in Apollo client, but both ways are now obsolete and replaced with official React Hooks. You can also check out this article on pros and cons of higher-order components, render props, and React Hooks (…)
From: https://atheros.ai/blog/react-hooks-in-apollo-client-for-graphql-queries-and-mutations
preparar el servidor tambien es una tarea de un frontend o eso ya depende de backend? en dado caso que sea otra tarea neustra que cursos me recomiendan tomar para aprender esa parte.
Este cuate se complica demasiado jajaja
Pero si tenemos diferentes puntos de acceso graphql ¿? como debemos utilizarlos si ahora en el index.js ya pones que el ApolloProvider tiene un client como hacer con varios¿?
Hola Devs:
-Aqui les traigo un poco de documentacion:HOC
hacemos la consulta
import { gql } from '@apollo/client'
export const getPhotos = gql`
query ($id:ID!){
photos(categoryId:$id) {
id
likes
src
liked
}
}
`
//$id variable
luego utilizamos el hook useQuery y recibe un segundo argumento que son las opciones, en esas opciones
tenemos un objeto que recibe las variables
import { PhotoCard, PropsPhotoCard } from "../PhotoCard";
import { getPhotos } from "../../graphql/querys/photos";
import {useQuery} from '@apollo/client'
export const ListOfPhotoCards =()=>{
//feching de datos con el hook useQuery
const {data,loading} = useQuery<{photos:PropsPhotoCard[]}>(getPhotos,{
variables:{ // var of query
id:"1"
}
});
//console.log(data)
const photos = data?.photos
return(
<ul>
{ loading && <li>Loading...</li>}
{ photos?.map(photo=>(
<PhotoCard {...photo} />
))}
</ul>
)
}
En vista de las actualizaciones de graphql a uso de hooks, tome la decision de crear un HOC para extender la funcionalidad de mi componente hijo y separandolo del padre quien se encarga de la conexion con graphql:
import React from 'react'
import { useQuery} from "@apollo/client";
export function GraphqlContainer(WrappedComponent, query, variables) {
return function GraphqlContainerWithQuery() {
const useQueryResult = useQuery(query, {
variables: { ...variables },
});
return (
<>
<WrappedComponent {...useQueryResult} />
</>
);
};
}
import React from "react";
import PhotoCard from "../PhotoCard/PhotoCard";
import { GraphqlContainer } from "../../containers/graphqlContainer";
import { gql } from "@apollo/client";
const withPhotos = gql`
query getPhotos($categoryId: ID) {
photos(categoryId: $categoryId) {
id
categoryId
src
likes
userId
liked
}
}
`;
function ListOfPhotoCardComponent({data, loading, error}) {
if (loading) return "Loading...";
if (error) return <pre>{error.message}</pre>;
return (
<>
{data.photos.map((photo) => (
<PhotoCard key={photo.id} {...photo} />
))}
</>
);
}
const ListOfPhotoCard = GraphqlContainer(ListOfPhotoCardComponent, withPhotos, { categoryId: 2})
export { ListOfPhotoCard };
emulando un poco como se hacia antes de graphql
Actualización a React 18 (Julio 2022)
Cambia la manera de iniciar la aplicación, utilizando ReactDOM.createRoot
en vez del viejo ReactDOM.render
.
El src/index.js
quedará asi:
// src/index.js
import React from 'react'
import ReactDOM from 'react-dom/client'
import { ApolloClient, ApolloProvider, InMemoryCache } from '@apollo/client'
import { App } from './App'
const client = new ApolloClient({
uri: 'https://direccion-API-de.vercel.app/graphql',
cache: new InMemoryCache()
})
// React 18 new way to mount the app...
const container = document.getElementById('app')
const root = ReactDOM.createRoot(container)
root.render(
<ApolloProvider client={client}>
<App />
</ApolloProvider>
)
Infortunadamente muchas de las cosas que dice el instructor acá, no se pueden realizar cuando se emplea typescript debido a la cantidad de types que se deben definir. El graphql en sus nuevas versiones no se puede definir fuera de una función de react component.
Les dejo la forma en que lo deja sin embargo dista mucho de lo que se explico en esta clase
Componente ListOfPhotoCards
import React, { FC } from "react";
import { PhotoCard } from "../PhotoCard";
import { Lista, Item } from "./styles";
import { useQuery } from "@apollo/client";
import { GET_PHOTOS } from "../hoc/withPhotos";
interface photos {
photos: {
id: string;
categoryId: string;
src: string;
likes: number;
userId: string;
liked: string;
}[];
}
type Props = {
categoryId: number;
};
interface photosVars {
categoryId: number;
}
export const ListPhotoCards: FC<Props> = ({ categoryId }) => {
const { loading, error, data } = useQuery<photos, photosVars>(GET_PHOTOS, { variables: { categoryId } });
if (loading) return <p>Loading...</p>;
if (error) return <p>Error :</p>;
const photos = data?.photos;
return (
<Lista>
{photos && photos.map((photo) => (
<Item key={photo.id}>
<PhotoCard {...photo} />
</Item>
))}
</Lista>
);
};
componente hoc
import { gql } from "@apollo/client"
export const GET_PHOTOS = gql`
query getPhotos($categoryId: ID) {
photos(categoryId: $categoryId) {
id
categoryId
src
likes
userId
liked
}
}
`;
Los componentes de orden superior son una técnica utilizada en React que nos permite reutilizar la lógica de los componentes. Más específicamente, un componente de orden superior es una función que toma un componente como argumento y devuelve un componente.
En mi caso no pude agregar la carpeta de container ya que no sé como, me gustaría que el curso estuviera actualizado… Solo pude llegar a seprar el hoc.
Me mareé un poco con tanto cambio de posición en los componentes…
Los containers definen de manera más ordenada la arquitectura, me agrada la separación de lógicas
Este curso es Genial, gracias Platzi, Gracias Midudev…!!
container: es el que se encarga de hacer el fetching de datos de nuestro componente
Para los que trabajamos con Hooks, supongo que seguimos con MV debido a que no usamos un HOC
v
¿Quieres ver más aportes, preguntas y respuestas de la comunidad?
o inicia sesión.