Ищете отличного (только удаленного) разработчика React? Посетите мой профиль в LinkedIn и поздоровайтесь! 😃

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

Я постараюсь сократить количество невыполненных обещаний в этом году на 1, а в этом преуспею. Он по-прежнему двузначный, но, эй, никто не идеален, верно? 😂

Один из основных выводов последней статьи заключался в том, что для его работы вам понадобятся как клиентский, так и серверный код GraphQL. Клиент отправляет запрос, а Сервер получает его, извлекает данные и отправляет их в качестве ответа.

Если вы хотите узнать все о синтаксисе клиентского GraphQL, ознакомьтесь с другой моей статьей на эту тему: Базовое руководство по клиентскому GraphQL для начинающих.

Начнем со стороны клиента

В этой статье мы сосредоточимся на стороне клиента.

страницы / index.jsx

В приведенном ниже коде мы настроили client с помощью apollo-boost, а затем предоставили это как опору для ApolloProvider. ApolloProvider аналогичен API контекста React и используется для того, чтобы сделать client доступным для любой части вашего приложения, расположенной ниже в дереве компонентов - в данном случае BookInfo. Конечно, любой из детей BookInfo также будет иметь доступ к client.

компоненты / BookInfo / BookInfo.jsx

В приведенном выше файле МНОГОЕГО происходит. Мы разберем его по частям. Прочтите код, и вы увидите следующие важные части:

  1. строка шаблона запроса GraphQL
  2. строка шаблона мутации GraphQL
  3. Компонент BookInfo
  4. ловушка useQuery, которая принимает строку шаблона Query как параметр
  5. ловушка useMutation, которая принимает строку шаблона Mutation как параметр
  6. функция updateCache, которая обновляет локальный кеш новыми данными
  7. функция с именем updateBookDetails, которая обертывает функцию мутации и передает ее компоненту, где она запускается через событие onClick

Хорошо, давайте пройдемся по всем частям по очереди:

1. Строка шаблона запроса

Мы используем функцию gql для создания запроса GraphQL, который запрашивает нужные нам данные. Этот запрос позже будет использоваться ловушкой useQuery. В частности, он запрашивает поля author и name объекта book.

Вы можете поместить в этот запрос все, о чем вы узнали из моей статьи Базовый клиентский GraphQL.

Вы можете прочитать больше о функции gql здесь, но вот небольшой отрывок из документации:

gql: Тег литерала шаблона JavaScript, который анализирует строки запроса GraphQL в стандартный GraphQL AST.

2. Строка шаблона мутации GraphQL

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

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

3. Компонент BookInfo

Это стандартный компонент React, но, несмотря на то, что он не принимает никаких реквизитов, он имеет доступ к клиенту, который мы определили в pages/index.jsx, поскольку он был заключен в ApolloProvider.

Вот почему нам не нужно определять конечную точку для useQuery или useMutation хуков или устанавливать новое соединение - у них уже есть доступ к этим данным через client, который доступен в контексте.

4. ловушка useQuery, которая принимает в качестве параметра строку шаблона Query.

Мы используем хук useQuery для запроса данных с сервера. Для этого мы снабжаем его запросом GraphQL с помощью функции gql.

После того, как ловушка запускается и Сервер что-то возвращает, ловушка возвращает объект, содержащий свойства loading, error и data, которые вы можете деструктурировать с помощью ES6. Вы можете использовать loading и error для проверки состояния запроса, как мы делали выше, и, конечно же, data будет содержать запрошенные нами данные, если все в порядке.

Если loading ложно, а error нет, то запрос завершен успешно.

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

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

Есть также другие параметры, которые useQuery может принимать, например variables и displayName. Чтобы увидеть их все, ознакомьтесь с полной useQuery документацией по API здесь.

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

const GET_BOOK_BY_AUTHOR = gql`
  query Book($author: String!) {
    book(author: $author) {
      name
      author
    }
  }
`;
const BookInfo = ({ author }) => {
  const { loading, error, data } = useQuery(GET_BOOK_DETAILS, { variables: { author },
});
  return (<p>{data.book.name} - {data.book.author}</p>)
})

Таким образом, мы берем author как опору для компонента, а затем передаем его как переменную ловушке useQuery во время рендеринга.

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

Наконец, useQuery запускается автоматически при рендеринге Компонента. Если вы хотите, чтобы запрос выполнялся позже, а НЕ во время рендеринга - например, когда Пользователь нажимает кнопку - вместо этого используйте useLazyQuery. useLazyQuery возвращает функцию, которая выполняет запрос всякий раз, когда вы его вызываете. Об этом читайте здесь.

5. ловушка useMutation, которая принимает строку шаблона Mutation в качестве параметра

Хук useMutation используется для запроса изменения данных на сервере (т. Е. Мутации). Как и useQuery, нам нужно предоставить ему строку шаблона мутации. В приведенном выше коде это SET_BOOK_DETAILS.

Теперь мы не хотим, чтобы запрос выполнялся автоматически при рендеринге компонента, т. Е. что делает хук useQuery. Вместо этого мы хотим иметь возможность выбрать точное время, когда он будет выполнен. Это может быть ответ на событие (например, пользователь нажимает кнопку) или что-то еще, что вам нравится. Вам решать.

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

const QUERY_SRTING = gql`some mutation`
const [updateBook, { 
  loading: mutationLoading, 
  error: mutationError 
}] = useMutation(QUERY_STRING)

6. функция updateCache, которая обновляет локальный кеш новыми данными

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

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

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

Обновление отдельного объекта

Если вы обновляете только один объект, и он уже существует в кеше, Apollo Client обновит его автоматически. Волшебство, которое происходит в фоновом режиме, заключается в том, что Apollo Server по умолчанию возвращает id объекта, а также значения, которые были изменены, а Apollo Client затем захватывает их и выполняет необходимое обновление кеша.

Обновление кеша вручную

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

const [updateBook] = useMutation(SET_BOOK_DETAILS, { update: updateCache });

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

const updateCache = (cache, { data: { updateBook } }) => {
    const existingBook = cache.readQuery({
      query: GET_BOOK_DETAILS,
    });
    cache.writeQuery({
      query: GET_BOOK_DETAILS,
      data: { book: updateBook },
    });
  };

Первое, что вы заметите, это то, что updateCache принимает параметр с именем cache, который является копией локального кэша клиента Apollo.

Есть два основных метода, которые существуют для объекта cache - readyQuery и writeQuery. Эти два метода позволяют вам читать и записывать в кеш.

Объект { data: { updateBook } }, который вы видите, который является вторым параметром, содержит возвращенный результат мутации, произошедшей на сервере.

Чтобы обновить кеш, мы сначала читаем содержимое кеша для запроса, на который мы хотим нацелить - в данном случае GET_BOOK_DETAILS. Затем мы сохраняем результат в переменной existingBook. Если бы мы собирались обновить несколько значений (например, добавить элемент в массив), мы бы использовали эту переменную для обновления данных так, как мы хотели (поскольку это всего лишь один элемент, мы ничего с ним не делаем) и используем его в writeQuery, а затем мы, наконец, используем writeQuery для перезаписи существующих данных в кэше.

7. функция updateBookDetails, которая обертывает функцию Mutation и передает ее компоненту, где она запускается через событие onClick.

Как следует из названия, мы оборачиваем функцию updateBook Mutation и вызываем ее в событии ниже. Конечно, вы можете пойти дальше и предоставить функцию обновления непосредственно событию, но я считаю, что это более беспорядочный подход. Это вызывает некоторое раздувание кода, и вы можете захотеть запустить какой-то другой код, прежде чем идти и выполнять его.

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

Заключение

В этой статье мы сосредоточились на клиентской стороне приложения Apollo. В моей следующей статье - части 3 этой серии - мы подробно рассмотрим серверную часть. Следите за обновлениями, и я обновлю эту статью со ссылкой на нее, когда она будет готова.

Хорошего дня! 😁