Entendendo o GraphQl do lado do cliente com aplicativos Apollo-Client em React

Publicados: 2022-03-10
Resumo rápido ↬ Já tentou interagir com um servidor GraphQL em uma aplicação do lado do cliente e sentiu vontade de desistir antes mesmo de chegar a algum lugar? Já recusou um convite para participar de uma base de código que requer trabalhar com a API GraphQL porque você não tinha ideia? Já se sentiu o único engenheiro de front-end que não aprendeu a consumir APIs GraphQL? Se você respondeu sim a alguma dessas perguntas, então este tutorial é para você. Daremos uma olhada em alguns conceitos básicos do GraphQL e do Apollo Client, bem como como trabalhar com ambos. No final, teremos construído um aplicativo de pet shop que usa o Apollo Client. Então, você pode continuar a construir seu próximo projeto.

De acordo com o State of JavaScript 2019, 38,7% dos desenvolvedores gostariam de usar o GraphQL, enquanto 50,8% dos desenvolvedores gostariam de aprender o GraphQL.

Sendo uma linguagem de consulta, o GraphQL simplifica o fluxo de trabalho de construção de um aplicativo cliente. Ele remove a complexidade do gerenciamento de endpoints de API em aplicativos do lado do cliente porque expõe um único endpoint HTTP para buscar os dados necessários. Assim, elimina o overfetching e underfetching de dados, como no caso do REST.

Mas o GraphQL é apenas uma linguagem de consulta. Para usá-lo facilmente, precisamos de uma plataforma que faça o trabalho pesado para nós. Uma dessas plataformas é a Apollo.

A plataforma Apollo é uma implementação do GraphQL que transfere dados entre a nuvem (o servidor) para a interface do usuário do seu aplicativo. Quando você usa o Apollo Client, toda a lógica para recuperar dados, rastrear, carregar e atualizar a interface do usuário é encapsulada pelo gancho useQuery (como no caso do React). Portanto, a busca de dados é declarativa. Ele também possui cache de configuração zero. Apenas configurando o Apollo Client em seu aplicativo, você obtém um cache inteligente pronto para uso, sem necessidade de configuração adicional.

O Apollo Client também é interoperável com outras estruturas, como Angular, Vue.js e React.

Nota : Este tutorial beneficiará aqueles que trabalharam com RESTful ou outras formas de APIs no passado no lado do cliente e desejam ver se vale a pena tentar o GraphQL. Isso significa que você deveria ter trabalhado com uma API antes; só então você será capaz de entender como o GraphQL pode ser benéfico para você. Embora estejamos cobrindo alguns conceitos básicos do GraphQL e do Apollo Client, um bom conhecimento de JavaScript e React Hooks será útil.

Noções básicas do GraphQL

Este artigo não é uma introdução completa ao GraphQL, mas definiremos algumas convenções antes de continuar.

O que é GraphQL?

GraphQL é uma especificação que descreve uma linguagem de consulta declarativa que seus clientes podem usar para solicitar a uma API os dados exatos que desejam. Isso é alcançado criando um esquema de tipo forte para sua API, com flexibilidade máxima. Ele também garante que a API resolva os dados e que as consultas do cliente sejam validadas em relação a um esquema. Essa definição significa que o GraphQL contém algumas especificações que o tornam uma linguagem de consulta declarativa, com uma API estaticamente tipada (construída em torno do Typescript) e possibilitando que o cliente aproveite esses sistemas de tipos para solicitar à API os dados exatos que deseja .

Então, se criamos alguns tipos com alguns campos neles, então, do lado do cliente, poderíamos dizer: “Dê-nos esses dados com esses campos exatos”. Em seguida, a API responderá com essa forma exata, como se estivéssemos usando um sistema de tipos em uma linguagem fortemente tipada. Você pode aprender mais no meu artigo Typescript.

Vejamos algumas convenções do GraphQl que nos ajudarão à medida que continuarmos.

O básico

  • Operações
    No GraphQL, toda ação realizada é chamada de operação. Existem algumas operações, a saber:
    • Consulta
      Esta operação está relacionada com a obtenção de dados do servidor. Você também pode chamá-lo de busca somente leitura.
    • Mutação
      Esta operação envolve a criação, atualização e exclusão de dados de um servidor. É popularmente chamado de operação CUD (criar, atualizar e excluir).
    • Assinaturas
      Essa operação no GraphQL envolve o envio de dados de um servidor para seus clientes quando ocorrem eventos específicos. Eles geralmente são implementados com WebSockets.

Neste artigo, estaremos lidando apenas com operações de consulta e mutação.

  • Nomes de operação
    Existem nomes exclusivos para suas operações de consulta e mutação do lado do cliente.
  • Variáveis ​​e argumentos
    As operações podem definir argumentos, muito parecido com uma função na maioria das linguagens de programação. Essas variáveis ​​podem então ser passadas para consultas ou chamadas de mutação dentro da operação como argumentos. Espera-se que as variáveis ​​sejam fornecidas em tempo de execução durante a execução de uma operação de seu cliente.
  • Alias
    Essa é uma convenção no GraphQL do lado do cliente que envolve a renomeação de nomes de campo detalhados ou vagos com nomes de campo simples e legíveis para a interface do usuário. O alias é necessário em casos de uso em que você não deseja ter nomes de campo conflitantes.
Convenções básicas do GraphQL
Convenções básicas do GraphQL. (Visualização grande)
Mais depois do salto! Continue lendo abaixo ↓

O que é o GraphQL do lado do cliente?

Quando um engenheiro de front-end cria componentes de interface do usuário usando qualquer estrutura, como Vue.js ou (no nosso caso) React, esses componentes são modelados e projetados a partir de um determinado padrão no cliente para se adequar aos dados que serão buscados no servidor.

Um dos problemas mais comuns com APIs RESTful é o overfetching e underfetching. Isso acontece porque a única maneira de um cliente fazer download de dados é acessando pontos de extremidade que retornam estruturas de dados fixas . A busca excessiva nesse contexto significa que um cliente baixa mais informações do que o exigido pelo aplicativo.

No GraphQL, por outro lado, você simplesmente enviaria uma única consulta ao servidor GraphQL que inclui os dados necessários. O servidor então responderia com um objeto JSON dos dados exatos que você solicitou - portanto, sem overfetching. Sebastian Eschweiler explica as diferenças entre APIs RESTful e GraphQL.

O GraphQL do lado do cliente é uma infraestrutura do lado do cliente que faz interface com dados de um servidor GraphQL para executar as seguintes funções:

  • Ele gerencia dados enviando consultas e alterando dados sem que você tenha que construir solicitações HTTP sozinho. Você pode gastar menos tempo canalizando dados e mais tempo construindo o aplicativo real.
  • Ele gerencia a complexidade de um cache para você. Assim, você pode armazenar e recuperar os dados obtidos do servidor, sem qualquer interferência de terceiros, e evitar facilmente a nova busca de recursos duplicados. Assim, ele identifica quando dois recursos são iguais, o que é ótimo para um aplicativo complexo.
  • Ele mantém sua UI consistente com a Optimistic UI, uma convenção que simula os resultados de uma mutação (ou seja, os dados criados) e atualiza a UI antes mesmo de receber uma resposta do servidor. Depois que a resposta é recebida do servidor, o resultado otimista é descartado e substituído pelo resultado real.

Para obter mais informações sobre o GraphQL do lado do cliente, reserve uma hora com o cocriador do GraphQL e outras pessoas legais no GraphQL Radio.

O que é o cliente Apollo?

O Apollo Client é um cliente GraphQL interoperável, ultraflexível e orientado para a comunidade para JavaScript e plataformas nativas. Seus recursos impressionantes incluem uma ferramenta robusta de gerenciamento de estado (Apollo Link), um sistema de cache de configuração zero, uma abordagem declarativa para buscar dados, paginação fácil de implementar e a interface de usuário otimista para seu aplicativo do lado do cliente.

O Apollo Client armazena não apenas o estado dos dados obtidos do servidor, mas também o estado que ele criou localmente em seu cliente; portanto, ele gerencia o estado dos dados da API e dos dados locais.

Também é importante observar que você pode usar o Apollo Client junto com outras ferramentas de gerenciamento de estado, como Redux, sem conflito. Além disso, é possível migrar seu gerenciamento de estado de, digamos, Redux para Apollo Client (que está além do escopo deste artigo). Em última análise, o principal objetivo do Apollo Client é permitir que os engenheiros consultem dados em uma API sem problemas.

Recursos do cliente Apollo

O Apollo Client conquistou muitos engenheiros e empresas por causa de seus recursos extremamente úteis que facilitam a criação de aplicativos modernos e robustos. Os seguintes recursos vêm incorporados:

  • Cache
    O Apollo Client oferece suporte ao cache em tempo real.
  • IU otimista
    O Apollo Client tem suporte legal para a interface do usuário otimista. Envolve a exibição temporária do estado final de uma operação (mutação) enquanto a operação está em andamento. Quando a operação estiver concluída, os dados reais substituem os dados otimistas.
  • Paginação
    O Apollo Client possui uma funcionalidade integrada que facilita bastante a implementação da paginação em seu aplicativo. Ele cuida da maioria das dores de cabeça técnicas de buscar uma lista de dados, seja em patches ou de uma só vez, usando a função fetchMore , que vem com o gancho useQuery .

Neste artigo, veremos uma seleção desses recursos.

Chega de teoria. Aperte o cinto e pegue uma xícara de café para acompanhar suas panquecas, pois sujamos as mãos.

Criando nosso aplicativo da Web

Este projeto é inspirado por Scott Moss.

Construiremos um aplicativo web simples para pet shop, cujos recursos incluem:

  • buscar nossos animais de estimação do lado do servidor;
  • criar um animal de estimação (que envolve a criação do nome, tipo de animal e imagem);
  • usando a interface do usuário otimista;
  • usando paginação para segmentar nossos dados.

Para começar, clone o repositório, garantindo que o branch starter seja o que você clonou.

Começando

  • Instale a extensão Apollo Client Developer Tools para Chrome.
  • Usando a interface de linha de comando (CLI), navegue até o diretório do repositório clonado e execute o comando para obter todas as dependências: npm install .
  • Execute o comando npm run app para iniciar o aplicativo.
  • Ainda na pasta raiz, execute o comando npm run server . Isso iniciará nosso servidor de back-end para nós, que usaremos à medida que prosseguirmos.

O aplicativo deve abrir em uma porta configurada. O meu é https://localhost:1234/ ; o seu é provavelmente outra coisa.

Se tudo funcionou bem, seu aplicativo deve ficar assim:

IU do branch inicial clonado
IU do branch inicial clonado. (Visualização grande)

Você notará que não temos animais de estimação para exibir. Isso porque ainda não criamos essa funcionalidade.

Se você instalou o Apollo Client Developer Tools corretamente, abra as ferramentas do desenvolvedor e clique no ícone da bandeja. Você verá “Apollo” e algo assim:

Ferramentas de desenvolvedor de cliente Apollo
Ferramentas do desenvolvedor do cliente Apollo. (Visualização grande)

Assim como as ferramentas de desenvolvedor Redux e React, usaremos as Apollo Client Developer Tools para escrever e testar nossas consultas e mutações. A extensão vem com o GraphQL Playground.

Buscando animais de estimação

Vamos adicionar a funcionalidade que busca animais de estimação. Vá para client/src/client.js . Vamos escrever o Apollo Client, vinculá-lo a uma API, exportá-lo como um cliente padrão e escrever uma nova consulta.

Copie o código a seguir e cole-o em 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

Aqui está uma explicação do que está acontecendo acima:

  • ApolloClient
    Esta será a função que envolve nosso aplicativo e, portanto, faz interface com o HTTP, armazena os dados em cache e atualiza a interface do usuário.
  • InMemoryCache
    Este é o armazenamento de dados normalizado no Apollo Client que ajuda a manipular o cache em nosso aplicativo.
  • HttpLink
    Esta é uma interface de rede padrão para modificar o fluxo de controle de solicitações do GraphQL e buscar resultados do GraphQL. Ele atua como middleware, buscando resultados do servidor GraphQL toda vez que o link é acionado. Além disso, é um bom substituto para outras opções, como Axios e window.fetch .
  • Declaramos uma variável de link que é atribuída a uma instância de HttpLink . É preciso uma propriedade uri e um valor para nosso servidor, que é https://localhost:4000/ .
  • Em seguida, há uma variável de cache que contém a nova instância de InMemoryCache .
  • A variável cliente também usa uma instância de ApolloClient e encapsula o link e o cache .
  • Por fim, exportamos o client para que possamos usá-lo em todo o aplicativo.

Antes de vermos isso em ação, precisamos garantir que todo o nosso aplicativo seja exposto ao Apollo e que nosso aplicativo possa receber dados obtidos do servidor e que possa alterar esses dados.

Para conseguir isso, vamos para 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 você notará no código destacado, envolvemos o componente App em ApolloProvider e passamos o cliente como um prop para o client . ApolloProvider é semelhante ao Context.Provider do React. Ele envolve seu aplicativo React e coloca o cliente em contexto, o que permite acessá-lo de qualquer lugar em sua árvore de componentes.

Para buscar nossos animais de estimação do servidor, precisamos escrever consultas que solicitem os campos exatos que desejamos. Vá para client/src/pages/Pets.js e copie e cole o seguinte código nele:

 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> ) }

Com alguns bits de código, podemos buscar os pets do servidor.

O que é gql?

É importante observar que as operações no GraphQL geralmente são objetos JSON escritos com graphql-tag e com acentos graves.

As tags gql são tags literais de modelo JavaScript que analisam as strings de consulta do GraphQL no GraphQL AST (árvore de sintaxe abstrata).

  • Operações de consulta
    Para buscar nossos animais de estimação do servidor, precisamos realizar uma operação de consulta.
    • Como estamos fazendo uma operação de query , precisamos especificar o type de operação antes de nomeá-la.
    • O nome da nossa consulta é GET_PETS . É uma convenção de nomenclatura do GraphQL usar camelCase para nomes de campo.
    • O nome dos nossos campos é pets . Portanto, especificamos os campos exatos que precisamos do servidor (id, name, type, img) .
    • useQuery é um gancho React que é a base para executar consultas em um aplicativo Apollo. Para realizar uma operação de consulta em nosso componente React, chamamos o hook useQuery , que foi inicialmente importado de @apollo/react-hooks . Em seguida, passamos uma string de consulta GraphQL, que é GET_PETS no nosso caso.
  • Quando nosso componente é renderizado, useQuery retorna uma resposta de objeto do Apollo Client que contém propriedades de carregamento, erro e dados. Assim, eles são desestruturados, para que possamos usá-los para renderizar a UI.
  • useQuery é incrível. Não precisamos incluir async-await . Já está resolvido em segundo plano. Bem legal, não é?
    • loading
      Essa propriedade nos ajuda a lidar com o estado de carregamento do aplicativo. No nosso caso, retornamos um componente Loader enquanto nosso aplicativo é carregado. Por padrão, o carregamento é false .
    • error
      Por precaução, usamos essa propriedade para lidar com qualquer erro que possa ocorrer.
    • data
      Este contém nossos dados reais do servidor.
    • Por fim, em nosso componente PetsList , passamos as props pets , com data.pets como um valor de objeto.

Neste ponto, consultamos nosso servidor com sucesso.

Para iniciar nossa aplicação, vamos executar o seguinte comando:

  • Inicie o aplicativo cliente. Execute o comando npm run app em sua CLI.
  • Inicie o servidor. Execute o comando npm run server em outra CLI.
CLI do VScode particionado para iniciar o cliente e o servidor.
CLI do VScode particionado para iniciar o cliente e o servidor. (Visualização grande)

Se tudo correu bem, você deve ver isso:

Animais de estimação consultados no servidor.
Animais de estimação consultados no servidor.

Dados em mutação

Mudar dados ou criar dados no Apollo Client é quase o mesmo que consultar dados, com pequenas alterações.

Ainda em client/src/pages/Pets.js , vamos copiar e colar o código destacado:

 .... 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 criar uma mutação, seguiríamos os seguintes passos.

1. mutation

Para criar, atualizar ou excluir, precisamos realizar a operação de mutation . A operação de mutation tem um nome CreateAPet , com um argumento. Este argumento tem uma variável $newPet , com um tipo de NewPetInput . O ! significa que a operação é necessária; assim, o GraphQL não executará a operação a menos que passemos uma variável newPet cujo tipo seja NewPetInput .

2. addPet

A função addPet , que está dentro da operação de mutation , recebe um argumento de input e é definida como nossa variável $newPet . Os conjuntos de campos especificados em nossa função addPet devem ser iguais aos conjuntos de campos em nossa consulta. Os conjuntos de campos em nossa operação são:

  • id
  • name
  • type
  • img

3. useMutation

O hook useMutation React é a API primária para executar mutações em um aplicativo Apollo. Quando precisamos alterar dados, chamamos useMutation em um componente React e passamos uma string GraphQL (no nosso caso, NEW_PETS ).

Quando nosso componente renderiza useMutation , ele retorna uma tupla (ou seja, um conjunto ordenado de dados constituindo um registro) em um array que inclui:

  • uma função mutate que podemos chamar a qualquer momento para executar a mutação;
  • um objeto com campos que representam o status atual da execução da mutação.

O gancho useMutation recebe uma string de mutação do GraphQL (que é NEW_PETS em nosso caso). Desestruturamos a tupla, que é a função ( createPet ) que irá alterar os dados e o campo do objeto ( newPets ).

4. createPet

Em nossa função onSubmit , logo após o estado setModal , definimos nosso createPet . Esta função recebe uma variable com uma propriedade de objeto de um valor definido como { newPet: input } . A input representa os vários campos de entrada em nosso formulário (como nome, tipo, etc.).

Feito isso, o resultado deve ficar assim:

Mutação sem atualização instantânea
Mutação sem atualização instantânea.

Se você observar o GIF de perto, perceberá que nosso bichinho criado não aparece instantaneamente, apenas quando a página é atualizada. No entanto, ele foi atualizado no servidor.

A grande questão é: por que nosso pet não atualiza instantaneamente? Vamos descobrir na próxima seção.

Cache no cliente Apollo

A razão pela qual nosso aplicativo não é atualizado automaticamente é que nossos dados recém-criados não correspondem aos dados de cache no Apollo Client. Portanto, há um conflito sobre o que exatamente precisa ser atualizado do cache.

Simplificando, se realizarmos uma mutação que atualiza ou exclui várias entradas (um nó), somos responsáveis ​​por atualizar quaisquer consultas que façam referência a esse nó, para que ele modifique nossos dados em cache para corresponder às modificações que uma mutação faz em nosso back- dados finais .

Mantendo o Cache Sincronizado

Existem algumas maneiras de manter nosso cache sincronizado cada vez que realizamos uma operação de mutação.

A primeira é buscar novamente as consultas correspondentes após uma mutação, usando a propriedade do objeto refetchQueries (a maneira mais simples).

Nota: Se fôssemos usar esse método, ele teria uma propriedade de objeto em nossa função createPet chamada refetchQueries e conteria uma matriz de objetos com um valor da consulta: refetchQueries: [{ query: GET_PETS }] .

Como nosso foco nesta seção não é apenas atualizar nossos pets criados na interface do usuário, mas manipular o cache, não usaremos esse método.

A segunda abordagem é usar a função de update . No Apollo Client, há uma função auxiliar de update que ajuda a modificar os dados de cache, para que sincronize com as modificações que uma mutação faz em nossos dados de back-end. Usando esta função, podemos ler e gravar no cache.

Atualizando o cache

Copie o seguinte código destacado e cole-o em 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] }, }); }, } );
 .....

A função de update recebe dois argumentos:

  • O primeiro argumento é o cache do Apollo Client.
  • A segunda é a resposta exata da mutação do servidor. Desestruturamos a propriedade data e a configuramos para nossa mutação ( addPet ).

Em seguida, para atualizar a função, precisamos verificar qual consulta precisa ser atualizada (no nosso caso, a consulta GET_PETS ) e ler o cache.

Em segundo lugar, precisamos escrever na query que foi lida, para que ela saiba que estamos prestes a atualizá-la. Fazemos isso passando um objeto que contém uma propriedade de objeto de query , com o valor definido para nossa operação de query ( GET_PETS ), e uma propriedade de data cujo valor é um objeto de pet e que possui um array da mutação addPet e uma cópia do dados do animal de estimação.

Se você seguiu essas etapas com cuidado, verá seus animais de estimação atualizados automaticamente à medida que os cria. Vejamos as mudanças:

Atualizações de animais de estimação instantaneamente
Atualizações de animais de estimação instantaneamente.

IU otimista

Muitas pessoas são grandes fãs de carregadores e spinners. Não há nada de errado em usar um carregador; existem casos de uso perfeitos em que um carregador é a melhor opção. Eu escrevi sobre carregadores versus spinners e seus melhores casos de uso.

Loaders e spinners realmente desempenham um papel importante no design de UI e UX, mas a chegada do Optimistic UI roubou os holofotes.

O que é UI otimista?

A IU otimista é uma convenção que simula os resultados de uma mutação (dados criados) e atualiza a IU antes de receber uma resposta do servidor. Depois que a resposta é recebida do servidor, o resultado otimista é descartado e substituído pelo resultado real.

No final, uma UI otimista nada mais é do que uma maneira de gerenciar o desempenho percebido e evitar estados de carregamento.

O Apollo Client tem uma maneira muito interessante de integrar a interface do usuário otimista. Ele nos dá um gancho simples que nos permite escrever no cache local após a mutação. Vamos ver como isso funciona!

Passo 1

Vá para client/src/client.js e adicione apenas o código destacado.

 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

A primeira etapa envolve o seguinte:

  • Importamos setContext de apollo-link-context . A função setContext recebe uma função de retorno de chamada e retorna uma promessa cujo setTimeout é definido como 800ms , para criar um atraso quando uma operação de mutação é executada.
  • O método ApolloLink.from garante que a atividade de rede que representa o link (nossa API) do HTTP seja atrasada.

Passo 2

A próxima etapa é usar o gancho de IU otimista. Deslize de volta para client/src/pages/Pets.js e adicione apenas o código destacado abaixo.

 ..... 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' } }
 }); } .....

O objeto optimisticResponse é usado se quisermos que a interface do usuário seja atualizada imediatamente quando criamos um animal de estimação, em vez de esperar pela resposta do servidor.

Os trechos de código acima incluem o seguinte:

  • __typename é injetado pelo Apollo na consulta para buscar o type das entidades consultadas. Esses tipos são usados ​​pelo Apollo Client para construir a propriedade id (que é um símbolo) para fins de cache no apollo-cache . Portanto, __typename é uma propriedade válida da resposta da consulta.
  • A mutação é definida como o __typename de optimisticResponse .
  • Assim como definido anteriormente, o nome da nossa mutação é addPet e o __typename é Pet .
  • A seguir estão os campos de nossa mutação que queremos que a resposta otimista atualize:
    • id
      Como não sabemos qual será o ID do servidor, criamos um usando Math.floor .
    • name
      Esse valor é definido como input.name .
    • type
      O valor do tipo é input.type .
    • img
      Agora, como nosso servidor gera imagens para nós, usamos um espaço reservado para imitar nossa imagem do servidor.

Esta foi realmente uma longa viagem. Se você chegou ao fim, não hesite em fazer uma pausa em sua cadeira com sua xícara de café.

Vamos dar uma olhada no nosso resultado. O repositório de suporte para este projeto está no GitHub. Clone e experimente.

Resultado final do aplicativo pet shop
Resultado final do nosso aplicativo.

Conclusão

Os incríveis recursos do Apollo Client, como a interface de usuário otimista e a paginação, tornam a criação de aplicativos do lado do cliente uma realidade.

Enquanto o Apollo Client funciona muito bem com outros frameworks, como Vue.js e Angular, os desenvolvedores do React têm Apollo Client Hooks e, portanto, não podem deixar de gostar de criar um ótimo aplicativo.

Neste artigo, nós apenas arranhamos a superfície. Dominar o Apollo Client exige prática constante. Então, vá em frente e clone o repositório, adicione paginação e brinque com os outros recursos que ele oferece.

Por favor, compartilhe seus comentários e experiências na seção de comentários abaixo. Também podemos discutir seu progresso no Twitter. Saúde!

Referências

  • “Client-Side GraphQL In React”, Scott Moss, Frontend Master
  • “Documentação”, Cliente Apollo
  • “A interface do usuário otimista com React”, Patryk Andrzejewski
  • “Verdadeiras mentiras de interfaces de usuário otimistas”, Smashing Magazine