Comprender GraphQl del lado del cliente con Apollo-Client en aplicaciones React

Publicado: 2022-03-10
Resumen rápido ↬ ¿Alguna vez intentó interactuar con un servidor GraphQL en una aplicación del lado del cliente y sintió ganas de darse por vencido incluso antes de llegar a alguna parte? ¿Alguna vez rechazó una invitación para unirse a una base de código que requiere trabajar con la API de GraphQL porque no tenía idea? ¿Alguna vez se sintió como el único ingeniero front-end que no aprendió a consumir las API de GraphQL? Si respondiste afirmativamente a alguna de estas preguntas, entonces este tutorial es para ti. Echaremos un vistazo más de cerca a algunos conceptos básicos de GraphQL y Apollo Client, así como a cómo trabajar con ambos. Al final, habremos creado una aplicación de tienda de mascotas que usa Apollo Client. Luego, puede continuar con la construcción de su próximo proyecto.

Según State of JavaScript 2019, al 38,7 % de los desarrolladores les gustaría usar GraphQL, mientras que al 50,8 % de los desarrolladores les gustaría aprender GraphQL.

Al ser un lenguaje de consulta, GraphQL simplifica el flujo de trabajo de creación de una aplicación cliente. Elimina la complejidad de administrar puntos finales de API en aplicaciones del lado del cliente porque expone un único punto final HTTP para obtener los datos necesarios. Por lo tanto, elimina la obtención excesiva y la obtención insuficiente de datos, como en el caso de REST.

Pero GraphQL es solo un lenguaje de consulta. Para usarlo fácilmente, necesitamos una plataforma que haga el trabajo pesado por nosotros. Una de esas plataformas es Apolo.

La plataforma Apollo es una implementación de GraphQL que transfiere datos entre la nube (el servidor) a la interfaz de usuario de su aplicación. Cuando usa Apollo Client, toda la lógica para recuperar datos, rastrear, cargar y actualizar la interfaz de usuario está encapsulada por el useQuery (como en el caso de React). Por lo tanto, la obtención de datos es declarativa. También tiene almacenamiento en caché de configuración cero. Con solo configurar Apollo Client en su aplicación, obtiene un caché inteligente listo para usar, sin necesidad de configuración adicional.

Apollo Client también es interoperable con otros marcos, como Angular, Vue.js y React.

Nota : este tutorial beneficiará a aquellos que han trabajado con RESTful u otras formas de API en el pasado en el lado del cliente y quieren ver si vale la pena probar GraphQL. Esto significa que debería haber trabajado con una API antes; solo entonces podrá comprender cuán beneficioso podría ser GraphQL para usted. Si bien cubriremos algunos conceptos básicos de GraphQL y Apollo Client, un buen conocimiento de JavaScript y React Hooks será útil.

Conceptos básicos de GraphQL

Este artículo no es una introducción completa a GraphQL, pero definiremos algunas convenciones antes de continuar.

¿Qué es GraphQL?

GraphQL es una especificación que describe un lenguaje de consulta declarativo que sus clientes pueden usar para solicitar a una API los datos exactos que desean. Esto se logra mediante la creación de un esquema de tipo sólido para su API, con la máxima flexibilidad. También garantiza que la API resuelva los datos y que las consultas de los clientes se validen con un esquema. Esta definición significa que GraphQL contiene algunas especificaciones que lo convierten en un lenguaje de consulta declarativo, con una API tipificada estáticamente (construida alrededor de Typescript) y que hace posible que el cliente aproveche esos sistemas de tipos para solicitar a la API los datos exactos que desea. .

Entonces, si creamos algunos tipos con algunos campos, entonces, desde el lado del cliente, podríamos decir: "Danos estos datos con estos campos exactos". Luego, la API responderá con esa forma exacta, como si estuviéramos usando un sistema de tipos en un lenguaje fuertemente tipado. Puede obtener más información en mi artículo de TypeScript.

Veamos algunas convenciones de GraphQl que nos ayudarán a medida que continuamos.

Los basicos

  • Operaciones
    En GraphQL, cada acción realizada se denomina operación. Hay algunas operaciones, a saber:
    • Consulta
      Esta operación tiene que ver con la obtención de datos del servidor. También podría llamarlo una búsqueda de solo lectura.
    • Mutación
      Esta operación implica crear, actualizar y eliminar datos de un servidor. Popularmente se denomina operación CUD (crear, actualizar y eliminar).
    • Suscripciones
      Esta operación en GraphQL consiste en enviar datos desde un servidor a sus clientes cuando ocurren eventos específicos. Suelen implementarse con WebSockets.

En este artículo, nos ocuparemos únicamente de las operaciones de consulta y mutación.

  • Nombres de operaciones
    Hay nombres únicos para las operaciones de consulta y mutación del lado del cliente.
  • Variables y argumentos
    Las operaciones pueden definir argumentos, muy parecido a una función en la mayoría de los lenguajes de programación. Esas variables luego se pueden pasar a llamadas de consulta o mutación dentro de la operación como argumentos. Se espera que las variables se proporcionen en tiempo de ejecución durante la ejecución de una operación de su cliente.
  • alias
    Esta es una convención en GraphQL del lado del cliente que implica cambiar el nombre de los campos detallados o imprecisos con nombres de campo simples y legibles para la interfaz de usuario. El alias es necesario en casos de uso en los que no desea tener nombres de campo en conflicto.
Convenciones básicas de GraphQL
Convenciones básicas de GraphQL. (Vista previa grande)
¡Más después del salto! Continúe leyendo a continuación ↓

¿Qué es GraphQL del lado del cliente?

Cuando un ingeniero front-end crea componentes de interfaz de usuario utilizando cualquier marco, como Vue.js o (en nuestro caso) React, esos componentes se modelan y diseñan a partir de un determinado patrón en el cliente para adaptarse a los datos que se obtendrán del servidor.

Uno de los problemas más comunes con las API RESTful es la obtención excesiva y la obtención insuficiente. Esto sucede porque la única forma en que un cliente puede descargar datos es accediendo a puntos finales que devuelven estructuras de datos fijas . La sobreexplotación en este contexto significa que un cliente descarga más información de la que requiere la aplicación.

En GraphQL, por otro lado, simplemente enviaría una sola consulta al servidor de GraphQL que incluye los datos requeridos. Luego, el servidor respondería con un objeto JSON de los datos exactos que ha solicitado; por lo tanto, no se extrae demasiado. Sebastian Eschweiler explica las diferencias entre las API RESTful y GraphQL.

GraphQL del lado del cliente es una infraestructura del lado del cliente que interactúa con los datos de un servidor GraphQL para realizar las siguientes funciones:

  • Gestiona los datos mediante el envío de consultas y la mutación de datos sin que tengas que crear solicitudes HTTP por tu cuenta. Puede dedicar menos tiempo a la instalación de datos y más tiempo a la creación de la aplicación real.
  • Gestiona la complejidad de un caché por ti. Por lo tanto, puede almacenar y recuperar los datos obtenidos del servidor, sin la interferencia de terceros, y evitar fácilmente la recuperación de recursos duplicados. Por lo tanto, identifica cuándo dos recursos son iguales, lo cual es excelente para una aplicación compleja.
  • Mantiene su interfaz de usuario coherente con la interfaz de usuario optimista, una convención que simula los resultados de una mutación (es decir, los datos creados) y actualiza la interfaz de usuario incluso antes de recibir una respuesta del servidor. Una vez que se recibe la respuesta del servidor, el resultado optimista se descarta y se reemplaza con el resultado real.

Para obtener más información sobre GraphQL del lado del cliente, dedique una hora con el cocreador de GraphQL y otras personas interesantes en GraphQL Radio.

¿Qué es el cliente Apollo?

Apollo Client es un cliente GraphQL interoperable, ultraflexible e impulsado por la comunidad para JavaScript y plataformas nativas. Sus características impresionantes incluyen una sólida herramienta de administración de estado (Apollo Link), un sistema de almacenamiento en caché de configuración cero, un enfoque declarativo para obtener datos, paginación fácil de implementar y la interfaz de usuario optimista para su aplicación del lado del cliente.

Apollo Client almacena no solo el estado de los datos obtenidos del servidor, sino también el estado que ha creado localmente en su cliente; por lo tanto, administra el estado tanto de los datos API como de los datos locales.

También es importante tener en cuenta que puede usar Apollo Client junto con otras herramientas de administración de estado, como Redux, sin conflicto. Además, es posible migrar su gestión de estado de, por ejemplo, Redux a Apollo Client (que está más allá del alcance de este artículo). En última instancia, el objetivo principal de Apollo Client es permitir que los ingenieros consulten datos en una API sin problemas.

Características del cliente Apollo

Apollo Client se ha ganado a tantos ingenieros y empresas debido a sus características extremadamente útiles que hacen que la creación de aplicaciones modernas y robustas sea muy sencilla. Las siguientes características vienen integradas:

  • almacenamiento en caché
    Apollo Client admite el almacenamiento en caché sobre la marcha.
  • IU optimista
    Apollo Client tiene un excelente soporte para la interfaz de usuario optimista. Implica mostrar temporalmente el estado final de una operación (mutación) mientras la operación está en curso. Una vez completada la operación, los datos reales reemplazan a los datos optimistas.
  • Paginación
    Apollo Client tiene una funcionalidad integrada que facilita la implementación de la paginación en su aplicación. Se ocupa de la mayoría de los dolores de cabeza técnicos de obtener una lista de datos, ya sea en parches o de una sola vez, utilizando la función fetchMore , que viene con el useQuery .

En este artículo, veremos una selección de estas características.

Basta de teoría. Abróchese el cinturón de seguridad y tome una taza de café para acompañar sus panqueques, mientras nos ensuciamos las manos.

Construyendo nuestra aplicación web

Este proyecto está inspirado en Scott Moss.

Construiremos una aplicación web simple para una tienda de mascotas, cuyas características incluyen:

  • ir a buscar a nuestras mascotas desde el lado del servidor;
  • crear una mascota (lo que implica crear el nombre, el tipo de mascota y la imagen);
  • utilizando la interfaz de usuario optimista;
  • usando la paginación para segmentar nuestros datos.

Para comenzar, clone el repositorio, asegurándose de que la rama starter sea la que ha clonado.

Empezando

  • Instale la extensión Apollo Client Developer Tools para Chrome.
  • Con la interfaz de línea de comandos (CLI), navegue hasta el directorio del repositorio clonado y ejecute el comando para obtener todas las dependencias: npm install .
  • Ejecute el comando npm run app para iniciar la aplicación.
  • Mientras aún está en la carpeta raíz, ejecute el comando npm run server . Esto iniciará nuestro servidor back-end para nosotros, que usaremos a medida que avancemos.

La aplicación debería abrirse en un puerto configurado. El mío es https://localhost:1234/ ; lo tuyo es probablemente otra cosa.

Si todo funcionó bien, su aplicación debería verse así:

Interfaz de usuario de rama de inicio clonada
Interfaz de usuario de rama de inicio clonada. (Vista previa grande)

Notarás que no tenemos mascotas para mostrar. Eso es porque aún no hemos creado esa funcionalidad.

Si ha instalado correctamente las herramientas de desarrollo de Apollo Client, abra las herramientas de desarrollo y haga clic en el icono de la bandeja. Verás "Apolo" y algo como esto:

Herramientas de desarrollo del cliente Apollo
Herramientas de desarrollo del cliente Apollo. (Vista previa grande)

Al igual que las herramientas de desarrollo de Redux y React, utilizaremos las herramientas de desarrollo de Apollo Client para escribir y probar nuestras consultas y mutaciones. La extensión viene con GraphQL Playground.

Ir a buscar mascotas

Agreguemos la funcionalidad que busca mascotas. Vaya a client/src/client.js . Estaremos escribiendo Apollo Client, vinculándolo a una API, exportándolo como un cliente predeterminado y escribiendo una nueva consulta.

Copie el siguiente código y péguelo en client.js :

 import { ApolloClient } from 'apollo-client' import { InMemoryCache } from 'apollo-cache-inmemory' import { HttpLink } from 'apollo-link-http' const link = new HttpLink({ uri: 'https://localhost:4000/' }) const cache = new InMemoryCache() const client = new ApolloClient({ link, cache }) export default client

Aquí hay una explicación de lo que está sucediendo arriba:

  • ApolloClient
    Esta será la función que envuelve nuestra aplicación y, por lo tanto, interactúa con HTTP, almacena en caché los datos y actualiza la interfaz de usuario.
  • InMemoryCache
    Este es el almacén de datos normalizados en Apollo Client que ayuda a manipular el caché en nuestra aplicación.
  • HttpLink
    Esta es una interfaz de red estándar para modificar el flujo de control de las solicitudes de GraphQL y obtener resultados de GraphQL. Actúa como middleware, obteniendo resultados del servidor GraphQL cada vez que se activa el enlace. Además, es un buen sustituto de otras opciones, como Axios y window.fetch .
  • Declaramos una variable de enlace que se asigna a una instancia de HttpLink . Toma una propiedad uri y un valor para nuestro servidor, que es https://localhost:4000/ .
  • Lo siguiente es una variable de caché que contiene la nueva instancia de InMemoryCache .
  • La variable de cliente también toma una instancia de ApolloClient y envuelve el link y el cache .
  • Por último, exportamos el client para poder usarlo en toda la aplicación.

Antes de que podamos ver esto en acción, debemos asegurarnos de que toda nuestra aplicación esté expuesta a Apollo y que nuestra aplicación pueda recibir datos obtenidos del servidor y que pueda mutar esos datos.

Para lograr esto, vayamos a client/src/index.js :

 import React from 'react' import ReactDOM from 'react-dom' import { BrowserRouter } from 'react-router-dom' import { ApolloProvider } from '@apollo/react-hooks' import App from './components/App' import client from './client' import './index.css' const Root = () => ( <BrowserRouter>
 <ApolloProvider client={client}> <App /> </ApolloProvider>
 </BrowserRouter> ); ReactDOM.render(<Root />, document.getElementById('app')) if (module.hot) { module.hot.accept() }

Como notará en el código resaltado, envolvimos el componente de la App en ApolloProvider y pasamos el cliente como accesorio al client . ApolloProvider es similar a Context.Provider de React. Envuelve su aplicación React y coloca al cliente en contexto, lo que le permite acceder a él desde cualquier parte de su árbol de componentes.

Para obtener nuestras mascotas del servidor, debemos escribir consultas que soliciten los campos exactos que queremos. Dirígete a client/src/pages/Pets.js y copia y pega el siguiente código en él:

 import React, {useState} from 'react' import gql from 'graphql-tag' import { useQuery, useMutation } from '@apollo/react-hooks' import PetsList from '../components/PetsList' import NewPetModal from '../components/NewPetModal' import Loader from '../components/Loader'

const GET_PETS = gql` query getPets { pets { id name type img } } `;

export default function Pets () { const [modal, setModal] = useState(false)
 const { loading, error, data } = useQuery(GET_PETS); if (loading) return <Loader />; if (error) return <p>An error occured!</p>;


 const onSubmit = input => { setModal(false) } if (modal) { return <NewPetModal onSubmit={onSubmit} onCancel={() => setModal(false)} /> } return ( <div className="page pets-page"> <section> <div className="row betwee-xs middle-xs"> <div className="col-xs-10"> <h1>Pets</h1> </div> <div className="col-xs-2"> <button onClick={() => setModal(true)}>new pet</button> </div> </div> </section> <section>
 <PetsList pets={data.pets}/>
 </section> </div> ) }

Con unos pocos bits de código, podemos obtener las mascotas del servidor.

¿Qué es gql?

Es importante tener en cuenta que las operaciones en GraphQL son generalmente objetos JSON escritos con graphql-tag y con acentos graves.

Las etiquetas gql son etiquetas literales de plantillas de JavaScript que analizan las cadenas de consulta de GraphQL en GraphQL AST (árbol de sintaxis abstracta).

  • operaciones de consulta
    Para recuperar nuestras mascotas del servidor, debemos realizar una operación de consulta.
    • Debido a que estamos realizando una operación de query , necesitamos especificar el type de operación antes de nombrarla.
    • El nombre de nuestra consulta es GET_PETS . Es una convención de nomenclatura de GraphQL usar camelCase para nombres de campo.
    • El nombre de nuestros campos es pets . Por lo tanto, especificamos los campos exactos que necesitamos del servidor (id, name, type, img) .
    • useQuery es un enlace de React que es la base para ejecutar consultas en una aplicación Apollo. Para realizar una operación de consulta en nuestro componente React, llamamos al useQuery , que inicialmente se importó desde @apollo/react-hooks . Luego, le pasamos una cadena de consulta de GraphQL, que es GET_PETS en nuestro caso.
  • Cuando nuestro componente se procesa, useQuery devuelve una respuesta de objeto de Apollo Client que contiene propiedades de carga, error y datos. Por lo tanto, se desestructuran para que podamos usarlos para representar la interfaz de usuario.
  • useQuery es increíble. No tenemos que incluir async-await . Ya está atendido en segundo plano. Bastante genial, ¿no?
    • loading
      Esta propiedad nos ayuda a manejar el estado de carga de la aplicación. En nuestro caso, devolvemos un componente Loader mientras se carga nuestra aplicación. De forma predeterminada, la carga es false .
    • error
      Por si acaso, usamos esta propiedad para manejar cualquier error que pueda ocurrir.
    • data
      Esto contiene nuestros datos reales del servidor.
    • Por último, en nuestro componente PetsList , pasamos los accesorios de pets , con data.pets como valor de objeto.

En este punto, hemos consultado con éxito nuestro servidor.

Para iniciar nuestra aplicación, ejecutemos el siguiente comando:

  • Inicie la aplicación cliente. Ejecute el comando npm run app en su CLI.
  • Inicie el servidor. Ejecute el comando npm run server en otra CLI.
VScode CLI particionada para iniciar tanto el cliente como el servidor.
VScode CLI particionada para iniciar tanto el cliente como el servidor. (Vista previa grande)

Si todo salió bien, deberías ver esto:

Mascotas consultadas desde el servidor.
Mascotas consultadas desde el servidor.

Datos mutantes

Mutar datos o crear datos en Apollo Client es casi lo mismo que consultar datos, con cambios muy leves.

Todavía en client/src/pages/Pets.js , copiemos y peguemos el código resaltado:

 .... const GET_PETS = gql` query getPets { pets { id name type img } } `;

const NEW_PETS = gql` mutation CreateAPet($newPet: NewPetInput!) { addPet(input: $newPet) { id name type img } } `;

 const Pets = () => { const [modal, setModal] = useState(false) const { loading, error, data } = useQuery(GET_PETS);
 const [createPet, newPet] = useMutation(NEW_PETS);
 const onSubmit = input => { setModal(false)
 createPet({ variables: { newPet: input } }); } if (loading || newPet.loading) return <Loader />; if (error || newPet.error) return <p>An error occured</p>;
  
 if (modal) { return <NewPetModal onSubmit={onSubmit} onCancel={() => setModal(false)} /> } return ( <div className="page pets-page"> <section> <div className="row betwee-xs middle-xs"> <div className="col-xs-10"> <h1>Pets</h1> </div> <div className="col-xs-2"> <button onClick={() => setModal(true)}>new pet</button> </div> </div> </section> <section> <PetsList pets={data.pets}/> </section> </div> ) } export default Pets

Para crear una mutación, seguiríamos los siguientes pasos.

1. mutation

Para crear, actualizar o eliminar, debemos realizar la operación de mutation . La operación de mutation tiene un nombre CreateAPet , con un argumento. Este argumento tiene una variable $newPet , con un tipo de NewPetInput . El ! significa que se requiere la operación; por lo tanto, GraphQL no ejecutará la operación a menos que pasemos una variable newPet cuyo tipo sea NewPetInput .

2. addPet

La función addPet , que está dentro de la operación de mutation , toma un argumento de input y se establece en nuestra variable $newPet . Los conjuntos de campos especificados en nuestra función addPet deben ser iguales a los conjuntos de campos de nuestra consulta. Los conjuntos de campos en nuestra operación son:

  • id
  • name
  • type
  • img

3. useMutation

El useMutation React es la API principal para ejecutar mutaciones en una aplicación Apollo. Cuando necesitamos mutar datos, llamamos a useMutation en un componente React y le pasamos una cadena GraphQL (en nuestro caso, NEW_PETS ).

Cuando nuestro componente representa useMutation , devuelve una tupla (es decir, un conjunto ordenado de datos que constituye un registro) en una matriz que incluye:

  • una función de mutate que podemos llamar en cualquier momento para ejecutar la mutación;
  • un objeto con campos que representan el estado actual de la ejecución de la mutación.

Al gancho useMutation se le pasa una cadena de mutación de GraphQL (que es NEW_PETS en nuestro caso). Desestructuramos la tupla, que es la función ( createPet ) que mutará los datos y el campo del objeto ( newPets ).

4. createPet

En nuestra función onSubmit , poco después del estado setModal , definimos nuestro createPet . Esta función toma una variable con una propiedad de objeto de un valor establecido en { newPet: input } . La input representa los diversos campos de entrada en nuestro formulario (como nombre, tipo, etc.).

Con eso hecho, el resultado debería verse así:

Mutación sin actualización instantánea
Mutación sin actualización instantánea.

Si observa el GIF de cerca, notará que nuestra mascota creada no aparece instantáneamente, solo cuando se actualiza la página. Sin embargo, se ha actualizado en el servidor.

La gran pregunta es, ¿por qué nuestra mascota no se actualiza al instante? Averigüémoslo en la siguiente sección.

Almacenamiento en caché en el cliente Apollo

La razón por la que nuestra aplicación no se actualiza automáticamente es que nuestros datos recién creados no coinciden con los datos de caché en Apollo Client. Por lo tanto, existe un conflicto en cuanto a qué es exactamente lo que debe actualizarse desde el caché.

En pocas palabras, si realizamos una mutación que actualiza o elimina varias entradas (un nodo), entonces somos responsables de actualizar cualquier consulta que haga referencia a ese nodo, de modo que modifique nuestros datos almacenados en caché para que coincidan con las modificaciones que hace una mutación en nuestro respaldo. datos finales .

Mantener el caché sincronizado

Hay algunas formas de mantener nuestro caché sincronizado cada vez que realizamos una operación de mutación.

La primera es recuperando las consultas coincidentes después de una mutación, utilizando la propiedad del objeto refetchQueries (la forma más sencilla).

Nota: si tuviéramos que usar este método, tomaría una propiedad de objeto en nuestra función createPet llamada refetchQueries , y contendría una matriz de objetos con un valor de la consulta: refetchQueries: [{ query: GET_PETS }] .

Debido a que nuestro enfoque en esta sección no es solo actualizar nuestras mascotas creadas en la interfaz de usuario, sino manipular el caché, no usaremos este método.

El segundo enfoque es utilizar la función de update . En Apollo Client, hay una función auxiliar de update que ayuda a modificar los datos de la memoria caché, para que se sincronice con las modificaciones que realiza una mutación en nuestros datos de back-end. Usando esta función, podemos leer y escribir en el caché.

Actualización del caché

Copie el siguiente código resaltado y péguelo en client/src/pages/Pets.js :

 ...... const Pets = () => { const [modal, setModal] = useState(false) const { loading, error, data } = useQuery(GET_PETS);
 const [createPet, newPet] = useMutation(NEW_PETS, { update(cache, { data: { addPet } }) { const data = cache.readQuery({ query: GET_PETS }); cache.writeQuery({ query: GET_PETS, data: { pets: [addPet, ...data.pets] }, }); }, } );
 .....

La función de update recibe dos argumentos:

  • El primer argumento es el caché de Apollo Client.
  • El segundo es la respuesta de mutación exacta del servidor. Desestructuramos la propiedad data y la establecemos en nuestra mutación ( addPet ).

A continuación, para actualizar la función, debemos verificar qué consulta debe actualizarse (en nuestro caso, la consulta GET_PETS ) y leer el caché.

En segundo lugar, debemos escribir en la query que se leyó, para que sepa que estamos a punto de actualizarla. Lo hacemos pasando un objeto que contiene una propiedad de objeto de query , con el valor establecido en nuestra operación de query ( GET_PETS ), y una propiedad data cuyo valor es un objeto pet y que tiene una matriz de la mutación addPet y una copia de la datos de la mascota.

Si siguió estos pasos cuidadosamente, debería ver que sus mascotas se actualizan automáticamente a medida que las crea. Echemos un vistazo a los cambios:

Actualizaciones de mascotas al instante
Actualizaciones de mascotas al instante.

IU optimista

Mucha gente es fanática de los cargadores y los spinners. No hay nada de malo en usar un cargador; hay casos de uso perfectos donde un cargador es la mejor opción. He escrito sobre cargadores versus spinners y sus mejores casos de uso.

De hecho, los cargadores y los spinners juegan un papel importante en el diseño de UI y UX, pero la llegada de Optimistic UI ha acaparado la atención.

¿Qué es la interfaz de usuario optimista?

La interfaz de usuario optimista es una convención que simula los resultados de una mutación (datos creados) y actualiza la interfaz de usuario antes de recibir una respuesta del servidor. Una vez que se recibe la respuesta del servidor, el resultado optimista se descarta y se reemplaza con el resultado real.

Al final, una interfaz de usuario optimista no es más que una forma de administrar el rendimiento percibido y evitar estados de carga.

Apollo Client tiene una forma muy interesante de integrar la interfaz de usuario optimista. Nos da un enlace simple que nos permite escribir en el caché local después de la mutación. ¡Vamos a ver cómo funciona!

Paso 1

Dirígete a client/src/client.js y agrega solo el código resaltado.

 import { ApolloClient } from 'apollo-client' import { InMemoryCache } from 'apollo-cache-inmemory' import { HttpLink } from 'apollo-link-http'
import { setContext } from 'apollo-link-context' import { ApolloLink } from 'apollo-link' const http = new HttpLink({ uri: "https://localhost:4000/" }); const delay = setContext( request => new Promise((success, fail) => { setTimeout(() => { success() }, 800) }) ) const link = ApolloLink.from([ delay, http ])
const cache = new InMemoryCache() const client = new ApolloClient({ link, cache }) export default client

El primer paso implica lo siguiente:

  • Importamos setContext desde apollo-link-context . La función setContext toma una función de devolución de llamada y devuelve una promesa cuyo setTimeout se establece en 800ms para crear un retraso cuando se realiza una operación de mutación.
  • El método ApolloLink.from asegura que la actividad de la red que representa el enlace (nuestra API) de HTTP se retrase.

Paso 2

El siguiente paso es usar el gancho de IU optimista. Vuelva a client/src/pages/Pets.js y agregue solo el código resaltado a continuación.

 ..... const Pets = () => { const [modal, setModal] = useState(false) const { loading, error, data } = useQuery(GET_PETS); const [createPet, newPet] = useMutation(NEW_PETS, { update(cache, { data: { addPet } }) { const data = cache.readQuery({ query: GET_PETS }); cache.writeQuery({ query: GET_PETS, data: { pets: [addPet, ...data.pets] }, }); }, } ); const onSubmit = input => { setModal(false) createPet({ variables: { newPet: input },
 optimisticResponse: { __typename: 'Mutation', addPet: { __typename: 'Pet', id: Math.floor(Math.random() * 10000 + ''), name: input.name, type: input.type, img: 'https://via.placeholder.com/200' } }
 }); } .....

El objeto optimisticResponse se usa si queremos que la interfaz de usuario se actualice inmediatamente cuando creamos una mascota, en lugar de esperar la respuesta del servidor.

Los fragmentos de código anteriores incluyen lo siguiente:

  • Apollo inyecta __typename en la consulta para obtener el type de las entidades consultadas. Apollo Client utiliza esos tipos para crear la propiedad id (que es un símbolo) con fines de almacenamiento en caché en apollo-cache . Entonces, __typename es una propiedad válida de la respuesta a la consulta.
  • La mutación se establece como el __typename de optimisticResponse .
  • Tal como se definió anteriormente, el nombre de nuestra mutación es addPet y el __typename es Pet .
  • Los siguientes son los campos de nuestra mutación que queremos que actualice la respuesta optimista:
    • id
      Debido a que no sabemos cuál será la ID del servidor, creamos una usando Math.floor .
    • name
      Este valor se establece en input.name .
    • type
      El valor del tipo es input.type .
    • img
      Ahora, debido a que nuestro servidor genera imágenes para nosotros, usamos un marcador de posición para imitar nuestra imagen del servidor.

Este fue de hecho un viaje largo. Si llegaste al final, no dudes en tomarte un descanso de tu silla con tu taza de café.

Echemos un vistazo a nuestro resultado. El repositorio de soporte para este proyecto está en GitHub. Clonar y experimentar con él.

Resultado final de la aplicación de la tienda de mascotas
Resultado final de nuestra aplicación.

Conclusión

Las increíbles funciones de Apollo Client, como la interfaz de usuario optimista y la paginación, hacen que la creación de aplicaciones del lado del cliente sea una realidad.

Si bien Apollo Client funciona muy bien con otros marcos, como Vue.js y Angular, los desarrolladores de React tienen Apollo Client Hooks, por lo que no pueden evitar disfrutar de la creación de una gran aplicación.

En este artículo, solo hemos arañado la superficie. Dominar Apollo Client exige una práctica constante. Entonces, continúe y clone el repositorio, agregue paginación y juegue con las otras funciones que ofrece.

Comparta sus comentarios y experiencia en la sección de comentarios a continuación. También podemos discutir su progreso en Twitter. ¡Salud!

Referencias

  • “GraphQL del lado del cliente en React”, Scott Moss, maestro de interfaz
  • “Documentación”, Cliente Apollo
  • “La interfaz de usuario optimista con React”, Patryk Andrzejewski
  • "Verdaderas mentiras de las interfaces de usuario optimistas", Smashing Magazine