Пошаговое руководство по созданию как интерфейса React, так и нашего собственного сервера GraphQL.

Примечание: все файлы (серверные и интерфейсные) можно найти на GitLab.

вступление

В этой статье мы:

  • Создайте наш собственный сервер GraphQL с разбивкой на страницы
  • Создайте интерфейс React с компонентом бесконечной прокрутки

Я видел несколько статей о бесконечной прокрутке в React, но очень немногие из них вдавались в подробности и не объясняли, как создать сервер GraphQL (большинство из них используют существующий сервер, такой как API GitHub GraphQL).

Давайте погрузимся в это. Мы начинаем с back-end и продолжаем front-end во второй половине, ниже.

Создание сервера GraphQL

Мы создадим apollo-server-express сервер GraphQL и настроим его с помощью следующих четырех файлов:

streetNames.json
Этот файл содержит данные, которые будут возвращены нашим сервером. Я создал фиктивные данные (100 названий улиц). Первые несколько строк выглядят так:

У каждой записи есть индекс, имя и хэш. Хеш формируется путем объединения индекса и имени в строку, а затем мы кодируем эту строку base64. Позже мы будем называть эти хеши курсорами. Запомни.

server.js
Этот файл загрузит наш сервер Apollo (GraphQL). Конфигурация очень проста:

Строка 6 очень интересна. Мы должны передать typeDefs (нашу схему GraphQL) и resolvers конструктору ApolloServer. Далее мы посмотрим на эти файлы.

Примечание: мы запускаем наш сервер с помощью nodemon (загружается более 3 миллионов раз в неделю!), который представляет собой инструмент, который автоматически перезагружает наш сервер при обнаружении изменений файлов. Это очень удобно во время разработки.
Полная команда для запуска нашего сервера:

nodemon -e js,graphql,json --exec babel-node server.js

Смотрите скрипт graphql:start (внутри package.json) в репозитории.

schema.graphql
Прежде чем мы рассмотрим схему в этом файле, несколько слов о разбивке на страницы с помощью GraphQL. Есть несколько способов создания схем с поддержкой разбивки на страницы. Но как говорят сами разработчики GraphQL (Facebook и сообщество разработчиков ПО с открытым исходным кодом):

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

Давайте не будем упрямиться и выберем разбиение на страницы на основе курсора. Что само по себе тоже можно сделать несколькими способами. Одной из популярных спецификаций является Спецификация соединений курсора GraphQL от Relay. Мы будем использовать это. Facebook является основным участником проекта с открытым исходным кодом Relay, который предоставляет эту спецификацию. Relay - это клиент GraphQL для React, используемый Facebook.com.

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

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

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

края в строке 8 - это в основном список с названиями наших улиц с некоторыми «метаданными» вокруг них. Каждая запись содержит курсор (который, как мы упоминали ранее, является хеш-значением записи). И он содержит актуальные данные, которые нас интересуют, в свойстве node, которое поступает из streetNames.json.

Обратите внимание, что этот ответ также указывает на то, что нужно получить больше данных. Вы можете увидеть это внутри pageInfo. Он показывает курсор последней возвращенной записи (который мы можем использовать в качестве параметра, когда мы будем делать наш следующий запрос), и он явно говорит, что можно получить больше данных ("hasNextPage": true).

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

resolvers.js
Когда наш сервер получает streetNames запрос, он должен разрешить этот запрос (он должен найти правильные данные). В нашем определении схемы мы могли видеть, что этот запрос может получать два (необязательных) параметра:

type Query {
  streetNames(first: Int, after: String): Response
}

Таким образом, мы можем настроить, сколько записей мы хотим получить, с first и after, какую запись она должна запускать при выборе данных. Значение after должно быть курсором. В нашем случае это хэш из файла JSON.

Наш резолвер (несколько упрощенный для удобства чтения - полная версия в репо):

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

Компонент React с бесконечной прокруткой

Прежде чем продолжить, убедитесь, что ваш локальный сервер GraphQL запущен. Если вы клонировали репозиторий, вы можете запустить:

$ yarn run graphql:start

Я создал новый проект с CRA (Create React App) и добавил некоторые зависимости. Мы будем использовать популярный Apollo Client для разговора с нашим сервером GraphQL:

$ npx create-react-app myApp
$ cd myApp
$ yarn add @apollo/client graphql

Экземпляр клиента Apollo

Сначала мы создаем и экспортируем экземпляр клиента внутри src/client.js:

В строке 7–13 мы настраиваем typePolicies для нашего запроса streetNames. Apollo-Client поддерживает это с версии 3 (выпущенной в июле 2020 г.). Здесь мы определяем как данные из последовательных вызовов схемы GraphQL, которая содержит список, объединяются (и сохраняются в кеше).

Другими словами, если мы сделаем два вызова нашему серверу:

  • вызов 1: получить записи от 0 до 9
  • звонок 2: получить записи с 10 по 19

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

Прежде чем двигаться дальше, убедитесь, что ваш основной компонент обернут в HOC (компонент высшего порядка) Apollo Provider со ссылкой на созданный экземпляр клиента:

Компонент бесконечной прокрутки

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

Сначала мы определяем наш запрос внутри queries.js:

Затем внутри нашего функционального компонента мы выполняем этот запрос с помощью хука useQuery из Apollo-Client:

first (10) настраивает, сколько «строк» ​​мы хотим получить за запрос. Помимо этого, параметр notifyOnNetworkStatusChange будет повторно отображать наш компонент при изменении значения networkStatus. По умолчанию это false, но мы хотим иметь возможность показывать в нашем компоненте, что «что-то происходит». Например, когда наши пользователи прокручивают страницу вниз и мы начинаем получать больше данных, мы можем показать счетчик.

Важно: обратите внимание, что useQuery также экспортирует переменную (функцию) с именем fetchMore. Эту функцию мы можем использовать для получения дополнительных данных!

Поэтому, когда мы отображаем наши данные (также возвращаемые useQuery; они содержат названия наших улиц) в виде строк в DOM, мы отобразим кнопку «загрузить еще» под ними:

Взгляните на нашу кнопку и ее обработчик кликов:

Обработчик кликов вызывает функцию fetchMore. Посмотрите, как мы передаем переменную after со значением endCursor из предыдущего запроса.

Теперь это должно иметь смысл.

Теперь вы можете открыть свой браузер и протестировать его. Это работает: мы можем загрузить больше данных, нажав кнопку «загрузить больше»!

Спасибо, что уделили время, и мы встретимся снова в следующий… подождите. Мы же не любим ручного труда, не так ли?

Поприветствуйте Intersection Observer API.

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

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

  • Создайте новый экземпляр IntersectionObserver
  • Позвольте ему наблюдать за кнопкой «загрузить еще».
  • Когда кнопка «загрузить больше» становится видимой (на экране), запускайте по ней событие щелчка.

Подробности можно найти в App.js в репозитории, внутри двух хуков useEffect в строках 20–37.

Теперь у нас есть компонент с бесконечной (автоматической) прокруткой и настраиваемый сервер GraphQL!

Спасибо за ваше время :)

Жерар ван дер Пут