Понимание GraphQl на стороне клиента с помощью Apollo-Client в приложениях React

Опубликовано: 2022-03-10
Краткое резюме ↬ Вы когда-нибудь пробовали взаимодействовать с сервером GraphQL в клиентском приложении и хотели сдаться еще до того, как что-то добились? Вы когда-нибудь отклоняли приглашение присоединиться к кодовой базе, которая требует работы с GraphQL API, потому что вы понятия не имели? Вы когда-нибудь чувствовали себя единственным фронтенд-инженером, который не научился использовать API-интерфейсы GraphQL? Если вы ответили утвердительно на любой из этих вопросов, то этот урок для вас. Мы более подробно рассмотрим несколько основ GraphQL и Apollo Client, а также то, как работать с ними обоими. К концу мы создадим приложение для зоомагазина, использующее Apollo Client. Затем вы можете приступить к созданию вашего следующего проекта.

Согласно State of JavaScript 2019, 38,7% разработчиков хотели бы использовать GraphQL, а 50,8% разработчиков хотели бы изучить GraphQL.

Будучи языком запросов, GraphQL упрощает рабочий процесс создания клиентского приложения. Это устраняет сложность управления конечными точками API в клиентских приложениях, поскольку предоставляет единую конечную точку HTTP для получения необходимых данных. Следовательно, он исключает перевыборку и недовыборку данных, как в случае с REST.

Но GraphQL — это всего лишь язык запросов. Чтобы использовать его легко, нам нужна платформа, которая сделает всю тяжелую работу за нас. Одной из таких платформ является Apollo.

Платформа Apollo — это реализация GraphQL, которая передает данные между облаком (сервером) и пользовательским интерфейсом вашего приложения. Когда вы используете Apollo Client, вся логика для получения данных, отслеживания, загрузки и обновления пользовательского интерфейса инкапсулируется хуком useQuery (как в случае с React). Следовательно, выборка данных является декларативной. Он также имеет кэширование с нулевой конфигурацией. Просто настроив Apollo Client в своем приложении, вы получаете интеллектуальный кэш «из коробки» без дополнительной настройки.

Клиент Apollo также совместим с другими фреймворками, такими как Angular, Vue.js и React.

Примечание . Этот учебник будет полезен тем, кто в прошлом работал с RESTful или другими формами API на стороне клиента и хочет узнать, стоит ли попробовать GraphQL. Это означает, что вы должны были раньше работать с API; только тогда вы сможете понять, насколько полезным для вас может быть GraphQL. Хотя мы рассмотрим несколько основ GraphQL и Apollo Client, вам пригодятся хорошие знания JavaScript и React Hooks.

Основы GraphQL

Эта статья не является полным введением в GraphQL, но мы определим несколько соглашений, прежде чем продолжить.

Что такое GraphQL?

GraphQL — это спецификация, описывающая декларативный язык запросов, который ваши клиенты могут использовать, чтобы запрашивать у API именно те данные, которые им нужны. Это достигается путем создания строгой схемы типов для вашего API с максимальной гибкостью. Это также гарантирует, что API разрешает данные и что клиентские запросы проверяются по схеме. Это определение означает, что GraphQL содержит некоторые спецификации, которые делают его декларативным языком запросов, с API, который является статически типизированным (построен на основе Typescript) и позволяет клиенту использовать эти системы типов, чтобы запрашивать у API точные данные, которые он хочет. .

Итак, если бы мы создали какие-то типы с некоторыми полями в них, то со стороны клиента мы могли бы сказать: «Дайте нам эти данные именно с этими полями». Тогда API ответит именно этой формой, как если бы мы использовали систему типов в строго типизированном языке. Вы можете узнать больше в моей статье Typescript.

Давайте рассмотрим некоторые соглашения GraphQl, которые помогут нам в дальнейшем.

Основы

  • Операции
    В GraphQL каждое выполняемое действие называется операцией. Есть несколько операций, а именно:
    • Запрос
      Эта операция связана с получением данных с сервера. Вы также можете назвать это выборкой только для чтения.
    • Мутация
      Эта операция включает в себя создание, обновление и удаление данных с сервера. В народе это называется операцией CUD (создание, обновление и удаление).
    • Подписки
      Эта операция в GraphQL включает отправку данных с сервера его клиентам при возникновении определенных событий. Обычно они реализуются с помощью WebSockets.

В этой статье мы будем иметь дело только с операциями запросов и мутаций.

  • Имена операций
    Существуют уникальные имена для клиентских запросов и операций мутации.
  • Переменные и аргументы
    Операции могут определять аргументы, как и функции в большинстве языков программирования. Затем эти переменные могут быть переданы в вызовы запросов или мутаций внутри операции в качестве аргументов. Ожидается, что переменные будут переданы во время выполнения во время выполнения операции от вашего клиента.
  • Псевдоним
    Это соглашение в GraphQL на стороне клиента, которое включает переименование подробных или расплывчатых имен полей с простыми и читаемыми именами полей для пользовательского интерфейса. Псевдонимы необходимы в случаях использования, когда вы не хотите иметь конфликтующие имена полей.
Основные соглашения GraphQL
Основные соглашения GraphQL. (Большой превью)
Еще после прыжка! Продолжить чтение ниже ↓

Что такое GraphQL на стороне клиента?

Когда фронтенд-инженер создает компоненты пользовательского интерфейса с использованием какой-либо платформы, такой как Vue.js или (в нашем случае) React, эти компоненты моделируются и проектируются на основе определенного шаблона на клиенте, чтобы соответствовать данным, которые будут получены с сервера.

Одной из наиболее распространенных проблем с RESTful API является избыточная и недостаточная выборка. Это происходит потому, что клиент может загрузить данные только путем обращения к конечным точкам, которые возвращают фиксированные структуры данных. Избыточная выборка в этом контексте означает, что клиент загружает больше информации, чем требуется приложению.

С другой стороны, в GraphQL вы просто отправляете один запрос на сервер GraphQL, который включает в себя необходимые данные. Затем сервер ответит объектом JSON с точными данными, которые вы запросили — следовательно, никакой избыточной выборки. Себастьян Эшвейлер объясняет различия между RESTful API и GraphQL.

GraphQL на стороне клиента — это инфраструктура на стороне клиента, которая взаимодействует с данными с сервера GraphQL для выполнения следующих функций:

  • Он управляет данными, отправляя запросы и изменяя данные без необходимости самостоятельно создавать HTTP-запросы. Вы можете тратить меньше времени на сбор данных и больше времени на создание фактического приложения.
  • Он управляет сложностью кэша для вас. Таким образом, вы можете хранить и извлекать данные, полученные с сервера, без вмешательства третьих лиц и легко избежать повторной загрузки дублирующихся ресурсов. Таким образом, он определяет, когда два ресурса совпадают, что отлично подходит для сложного приложения.
  • Он обеспечивает согласованность пользовательского интерфейса с оптимистичным пользовательским интерфейсом — соглашением, которое имитирует результаты мутации (т. е. созданные данные) и обновляет пользовательский интерфейс еще до получения ответа от сервера. Как только ответ получен от сервера, оптимистичный результат отбрасывается и заменяется фактическим результатом.

Для получения дополнительной информации о GraphQL на стороне клиента уделите час разговору с одним из создателей GraphQL и другими крутыми людьми на GraphQL Radio.

Что такое клиент Apollo?

Apollo Client — это интероперабельный, сверхгибкий клиент GraphQL, управляемый сообществом, для JavaScript и нативных платформ. Его впечатляющие функции включают надежный инструмент управления состоянием (Apollo Link), систему кэширования с нулевой конфигурацией, декларативный подход к выборке данных, простое в реализации разбиение на страницы и оптимистичный пользовательский интерфейс для вашего клиентского приложения.

Клиент Apollo хранит не только состояние из данных, полученных с сервера, но и состояние, созданное им локально на вашем клиенте; следовательно, он управляет состоянием как данных API, так и локальных данных.

Также важно отметить, что вы можете использовать Apollo Client вместе с другими инструментами управления состоянием, такими как Redux, без конфликтов. Кроме того, можно перенести управление состоянием, скажем, из Redux в Apollo Client (что выходит за рамки этой статьи). В конечном счете, основная цель Apollo Client — предоставить инженерам возможность беспрепятственно запрашивать данные в API.

Особенности клиента Аполлона

Клиент Apollo завоевал сердца многих инженеров и компаний благодаря своим чрезвычайно полезным функциям, которые упрощают создание современных надежных приложений. Встроены следующие функции:

  • Кэширование
    Клиент Apollo поддерживает кэширование «на лету».
  • Оптимистичный интерфейс
    У Apollo Client отличная поддержка Optimistic UI. Он включает временное отображение конечного состояния операции (мутации) во время выполнения операции. После завершения операции реальные данные заменяют оптимистичные данные.
  • Пагинация
    Клиент Apollo имеет встроенную функциональность, которая упрощает реализацию разбиения на страницы в вашем приложении. Он берет на себя большую часть технических проблем, связанных с получением списка данных, либо в патчах, либо сразу, используя функцию fetchMore , которая поставляется с хуком useQuery .

В этой статье мы рассмотрим некоторые из этих функций.

Хватит теории. Затяните ремень безопасности и возьмите чашку кофе к блинчикам, пока мы пачкаем руки.

Создание нашего веб-приложения

Этот проект вдохновлен Скоттом Моссом.

Мы будем создавать простое веб-приложение для зоомагазина, функции которого включают:

  • получение наших питомцев со стороны сервера;
  • создание питомца (что включает в себя создание имени, типа питомца и изображения);
  • использование оптимистичного пользовательского интерфейса;
  • использование разбиения на страницы для сегментации наших данных.

Для начала клонируйте репозиторий, убедившись, что starter ветка — это то, что вы клонировали.

Начиная

  • Установите расширение Apollo Client Developer Tools для Chrome.
  • Используя интерфейс командной строки (CLI), перейдите в каталог клонированного репозитория и выполните команду для получения всех зависимостей: npm install .
  • Запустите команду npm run app чтобы запустить приложение.
  • Находясь в корневой папке, запустите команду npm run server . Это запустит для нас наш внутренний сервер, который мы будем использовать в дальнейшем.

Приложение должно открыться в настроенном порту. У меня https://localhost:1234/ ; у вас, наверное, что-то другое.

Если все работает хорошо, ваше приложение должно выглядеть так:

Клонированный интерфейс стартовой ветки
Клонированный интерфейс стартовой ветки. (Большой превью)

Вы заметите, что у нас нет домашних животных для отображения. Это потому, что мы еще не создали такой функционал.

Если вы правильно установили инструменты разработчика Apollo Client, откройте инструменты разработчика и щелкните значок на панели задач. Вы увидите «Аполлон» и что-то вроде этого:

Инструменты разработчика клиентов Apollo
Инструменты разработчика клиентов Apollo. (Большой превью)

Как и инструменты разработчика Redux и React, мы будем использовать инструменты разработчика клиента Apollo для написания и тестирования наших запросов и мутаций. Расширение поставляется с игровой площадкой GraphQL.

Привлечение домашних животных

Давайте добавим функциональность, которая извлекает домашних животных. Перейдите в client/src/client.js . Мы напишем клиент Apollo, свяжем его с API, экспортируем в качестве клиента по умолчанию и напишем новый запрос.

Скопируйте следующий код и вставьте его в 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

Вот объяснение того, что происходит выше:

  • ApolloClient
    Это будет функция, которая обертывает наше приложение и, таким образом, взаимодействует с HTTP, кэширует данные и обновляет пользовательский интерфейс.
  • InMemoryCache
    Это нормализованное хранилище данных в Apollo Client, которое помогает манипулировать кешем в нашем приложении.
  • HttpLink
    Это стандартный сетевой интерфейс для изменения потока управления запросами GraphQL и получения результатов GraphQL. Он действует как промежуточное ПО, извлекая результаты с сервера GraphQL каждый раз при активации ссылки. Кроме того, это хорошая замена другим параметрам, таким как Axios и window.fetch .
  • Мы объявляем переменную ссылки, которая назначается экземпляру HttpLink . Он принимает свойство uri и значение для нашего сервера: https://localhost:4000/ .
  • Далее идет переменная кэша, содержащая новый экземпляр InMemoryCache .
  • Клиентская переменная также принимает экземпляр ApolloClient и заключает в себе link и cache .
  • Наконец, мы экспортируем client , чтобы использовать его во всем приложении.

Прежде чем мы увидим это в действии, мы должны убедиться, что все наше приложение доступно для Apollo и что наше приложение может получать данные, полученные с сервера, и что оно может изменять эти данные.

Для этого давайте перейдем к 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() }

Как вы заметили в выделенном коде, мы обернули компонент App в ApolloProvider и передали client в качестве реквизита client . ApolloProvider похож на Context.Provider React. Он обертывает ваше приложение React и помещает клиента в контекст, что позволяет вам получить к нему доступ из любой точки вашего дерева компонентов.

Чтобы получить наших питомцев с сервера, нам нужно написать запросы, запрашивающие именно те поля , которые нам нужны. Перейдите в client/src/pages/Pets.js и скопируйте и вставьте в него следующий код:

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

С помощью нескольких битов кода мы можем получать питомцев с сервера.

Что такое gql?

Важно отметить, что операции в GraphQL обычно представляют собой объекты JSON, написанные с graphql-tag и обратными кавычками.

Теги gql — это литеральные теги шаблона JavaScript, которые анализируют строки запроса GraphQL в AST GraphQL (абстрактное синтаксическое дерево).

  • Операции запроса
    Чтобы получить наших питомцев с сервера, нам нужно выполнить операцию запроса.
    • Поскольку мы выполняем операцию query , нам нужно было указать type операции, прежде чем называть ее.
    • Имя нашего запроса — GET_PETS . Соглашение об именах GraphQL заключается в использовании camelCase для имен полей.
    • Название наших полей - pets . Следовательно, мы указываем именно те поля, которые нам нужны от сервера (id, name, type, img) .
    • useQuery — это хук React, который является основой для выполнения запросов в приложении Apollo. Чтобы выполнить операцию запроса в нашем компоненте React, мы вызываем хук useQuery , который изначально был импортирован из @apollo/react-hooks . Затем мы передаем ему строку запроса GET_PETS , в нашем случае это GET_PETS.
  • Когда наш компонент отображается, useQuery возвращает объектный ответ от клиента Apollo, который содержит свойства загрузки, ошибки и данных. Таким образом, они деструктурированы, чтобы мы могли использовать их для рендеринга пользовательского интерфейса.
  • useQuery великолепен. Нам не нужно включать async-await . Об этом уже позаботились в фоновом режиме. Довольно круто, не правда ли?
    • loading
      Это свойство помогает нам обрабатывать состояние загрузки приложения. В нашем случае мы возвращаем компонент Loader во время загрузки нашего приложения. По умолчанию загрузка имеет значение false .
    • error
      На всякий случай мы используем это свойство для обработки любой ошибки, которая может возникнуть.
    • data
      Это содержит наши фактические данные с сервера.
    • Наконец, в нашем компоненте PetsList мы передаем свойства pets с data.pets в качестве значения объекта.

На данный момент мы успешно запросили наш сервер.

Чтобы запустить наше приложение, давайте запустим следующую команду:

  • Запустите клиентское приложение. Запустите команду npm run app в CLI.
  • Запустите сервер. Запустите команду npm run server в другом интерфейсе командной строки.
VScode CLI разбит на разделы для запуска как клиента, так и сервера.
VScode CLI разбит на разделы для запуска как клиента, так и сервера. (Большой превью)

Если все прошло хорошо, вы должны увидеть это:

Домашние животные запрошены с сервера.
Домашние животные запрошены с сервера.

Изменение данных

Изменение данных или создание данных в Apollo Client почти то же самое, что и запрос данных, с очень небольшими изменениями.

По-прежнему в client/src/pages/Pets.js давайте скопируем и вставим выделенный код:

 .... 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

Чтобы создать мутацию, мы предпримем следующие шаги.

1. mutation

Чтобы создать, обновить или удалить, нам нужно выполнить операцию mutation . Операция mutation имеет имя CreateAPet с одним аргументом. Этот аргумент имеет переменную $newPet с типом NewPetInput . ! означает, что операция требуется; таким образом, GraphQL не выполнит операцию, если мы не передаем переменную newPet , тип которой — NewPetInput .

2. addPet

Функция addPet , которая находится внутри операции mutation , принимает input аргумент и устанавливается в нашу переменную $newPet . Наборы полей, указанные в нашей функции addPet , должны совпадать с наборами полей в нашем запросе. Наборы полей в нашей операции:

  • id
  • name
  • type
  • img

3. useMutation

useMutation React — это основной API для выполнения мутаций в приложении Apollo. Когда нам нужно изменить данные, мы вызываем useMutation в компоненте React и передаем ему строку GraphQL (в нашем случае NEW_PETS ).

Когда наш компонент отображает useMutation , он возвращает кортеж (то есть упорядоченный набор данных, составляющих запись) в массиве, который включает:

  • функция mutate , которую мы можем вызвать в любое время для выполнения мутации;
  • объект с полями, представляющими текущий статус выполнения мутации.

В хук useMutation передается строка мутации NEW_PETS ). Мы деструктурировали кортеж, то есть функцию ( createPet ), которая изменяет данные и поле объекта ( newPets ).

4. createPet

В нашей функции onSubmit вскоре после состояния setModal мы определили наш createPet . Эта функция принимает variable со свойством объекта со значением, установленным в { newPet: input } . input представляет собой различные поля ввода в нашей форме (например, имя, тип и т. д.).

После этого результат должен выглядеть следующим образом:

Мутация без мгновенного обновления
Мутация без мгновенного обновления.

Если вы внимательно посмотрите на GIF, вы заметите, что созданный нами питомец не появляется мгновенно, а только при обновлении страницы. Тем не менее, он был обновлен на сервере.

Большой вопрос, почему наш питомец не обновляется мгновенно? Давайте узнаем в следующем разделе.

Кэширование в клиенте Apollo

Причина, по которой наше приложение не обновляется автоматически, заключается в том, что наши вновь созданные данные не соответствуют данным кеша в Apollo Client. Итак, возникает конфликт, что именно нужно обновить из кеша.

Проще говоря, если мы выполняем мутацию, которая обновляет или удаляет несколько записей (узел), то мы несем ответственность за обновление любых запросов, ссылающихся на этот узел, чтобы он модифицировал наши кэшированные данные, чтобы они соответствовали изменениям, которые мутация вносит в нашу обратную связь . конечные данные.

Хранение кеша в синхронизации

Есть несколько способов синхронизировать наш кеш каждый раз, когда мы выполняем операцию мутации.

Первый — повторная выборка соответствующих запросов после мутации с использованием свойства объекта refetchQueries (самый простой способ).

Примечание. Если бы мы использовали этот метод, он принял бы свойство объекта в нашей функции createPet именем refetchQueries и содержал бы массив объектов со значением запроса: refetchQueries: [{ query: GET_PETS }] .

Поскольку наше внимание в этом разделе сосредоточено не только на обновлении созданных нами питомцев в пользовательском интерфейсе, но и на управлении кешем, мы не будем использовать этот метод.

Второй подход заключается в использовании функции update . В Apollo Client есть вспомогательная функция update , которая помогает изменять данные кэша, чтобы они синхронизировались с изменениями, которые мутация вносит в наши внутренние данные. Используя эту функцию, мы можем читать и писать в кеш.

Обновление кеша

Скопируйте следующий выделенный код и вставьте его в 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] }, }); }, } );
 .....

Функция update получает два аргумента:

  • Первый аргумент — это кеш от Apollo Client.
  • Второй — это точный ответ мутации от сервера. Мы деструктурируем свойство data и устанавливаем для него нашу мутацию ( addPet ).

Далее, чтобы обновить функцию, нам нужно проверить, какой запрос нужно обновить (в нашем случае запрос GET_PETS ) и прочитать кеш.

Во-вторых, нам нужно написать в прочитанный query , чтобы он знал, что мы собираемся его обновить. Мы делаем это, передавая объект, который содержит свойство объекта query со значением, установленным для нашей операции query ( GET_PETS ), и свойство data , значение которого является объектом- pet и имеет массив мутации addPet и копию данные питомца.

Если вы внимательно выполнили эти шаги, вы должны увидеть, как ваши питомцы автоматически обновляются по мере их создания. Давайте посмотрим на изменения:

Домашние животные обновляются мгновенно
Домашние животные обновляются мгновенно.

Оптимистичный интерфейс

Многие люди являются большими поклонниками лоадеров и спиннеров. Нет ничего плохого в использовании загрузчика; есть идеальные варианты использования, когда загрузчик является лучшим вариантом. Я писал о загрузчиках и счетчиках и их лучших вариантах использования.

Загрузчики и спиннеры действительно играют важную роль в дизайне UI и UX, но появление Optimistic UI привлекло к себе всеобщее внимание.

Что такое оптимистичный интерфейс?

Оптимистичный пользовательский интерфейс — это соглашение, которое имитирует результаты мутации (созданные данные) и обновляет пользовательский интерфейс перед получением ответа от сервера. Как только ответ получен от сервера, оптимистичный результат отбрасывается и заменяется фактическим результатом.

В конце концов, оптимистичный пользовательский интерфейс — это не что иное, как способ управлять воспринимаемой производительностью и избегать состояний загрузки.

В Apollo Client реализован очень интересный способ интеграции пользовательского интерфейса Optimistic. Это дает нам простой хук, который позволяет нам писать в локальный кеш после мутации. Давайте посмотрим, как это работает!

Шаг 1

Перейдите к client/src/client.js и добавьте только выделенный код.

 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

Первый шаг включает в себя следующее:

  • Мы импортируем setContext из apollo-link-context . Функция setContext принимает функцию обратного вызова и возвращает обещание, для которого setTimeout установлено значение 800ms , чтобы создать задержку при выполнении операции мутации.
  • Метод ApolloLink.from гарантирует, что сетевая активность, представляющая ссылку (наш API) из HTTP , задерживается.

Шаг 2

Следующий шаг — использование хука Optimistic UI. Вернитесь к 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] }, }); }, } ); 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' } }
 }); } .....

Объект optimisticResponse используется, если мы хотим, чтобы пользовательский интерфейс обновлялся сразу же после создания питомца, а не ждал ответа сервера.

Приведенные выше фрагменты кода включают следующее:

  • __typename Apollo в запрос для получения type запрошенных сущностей. Эти типы используются клиентом Apollo для создания свойства id (которое является символом) для целей кэширования в apollo-cache . Таким образом, __typename является допустимым свойством ответа на запрос.
  • Мутация устанавливается как __typename из optimisticResponse .
  • Как было определено ранее, имя нашей мутации — addPet , а __typenamePet .
  • Далее идут поля нашей мутации, которые мы хотим обновить оптимистичным ответом:
    • id
      Поскольку мы не знаем, каким будет идентификатор сервера, мы создали его с помощью Math.floor .
    • name
      Это значение равно input.name .
    • type
      Значение типа — input.type .
    • img
      Теперь, поскольку наш сервер генерирует изображения для нас, мы использовали заполнитель для имитации нашего изображения с сервера.

Это была действительно долгая поездка. Если вы дочитали до конца, не стесняйтесь оторваться от стула с чашечкой кофе.

Давайте посмотрим на наш результат. Вспомогательный репозиторий для этого проекта находится на GitHub. Клонируйте и экспериментируйте с ним.

Окончательный результат приложения зоомагазина
Окончательный результат нашего приложения.

Заключение

Удивительные функции Apollo Client, такие как оптимистичный пользовательский интерфейс и нумерация страниц, делают создание клиентских приложений реальностью.

В то время как Apollo Client очень хорошо работает с другими фреймворками, такими как Vue.js и Angular, у разработчиков React есть хуки Apollo Client, поэтому они не могут не получать удовольствие от создания отличного приложения.

В этой статье мы только коснулись поверхности. Освоение Apollo Client требует постоянной практики. Итак, продолжайте и клонируйте репозиторий, добавьте разбивку на страницы и поэкспериментируйте с другими функциями, которые он предлагает.

Пожалуйста, поделитесь своими отзывами и опытом в разделе комментариев ниже. Мы также можем обсудить ваш прогресс в Твиттере. Ваше здоровье!

использованная литература

  • «Клиентский GraphQL в React», Скотт Мосс, Frontend Master
  • «Документация», клиент Apollo
  • «Оптимистичный пользовательский интерфейс с React», Патрик Анджеевски
  • «Правдивая ложь оптимистичных пользовательских интерфейсов», Smashing Magazine