Статьи

Кодирование приложения с помощью GraphQL, React Native и AWS AppSync: приложение

Конечный продукт
Что вы будете создавать

В этих руководствах я покажу вам, как создавать базы данных GraphQL и взаимодействовать с ними с помощью AWS AppSync и React Native. Это приложение будет работать в режиме реального времени и в автономном режиме, что мы получим из коробки с AppSync.

В предыдущем посте мы настраивали наш бэкэнд GraphQL с сервисом Amazon AppSync . Проверьте это, если вы еще этого не сделали. Или, если вы хотите ознакомиться с GraphQL , взгляните на некоторые другие наши посты.

  • GraphQL
    Кодирование приложения с помощью GraphQL, React Native и AWS AppSync: серверная часть
  • JavaScript
    Что такое GraphQL?
    Гиги Сайфан

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

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

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

У нас также будет папка ресурсов для хранения наших изображений.

структура папок

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

Сначала мы создали новое приложение React Native с помощью Expo .

Оказавшись в только что созданном проекте, мы установили наши зависимости. Для функциональности GraphQL и AppSync мы использовали следующие зависимости:

1
2
3
4
5
aws-appsync
aws-appsync-react
graphql-tag
react-apollo
uuid

Мы также использовали следующие зависимости для дизайна пользовательского интерфейса:

1
2
3
react-navigation
react-native-elements
react-native-vector-icons

Кроме того, после установки библиотеки векторных иконок мы связали ее:

1
react-native link react-native vector-icons

После установки наших зависимостей мы загрузили файл AppSync.js из нашей консоли AppSync. В нашей консоли проекта AppSync мы выбрали React Native внизу и нажали оранжевую кнопку « Загрузить» , чтобы загрузить этот файл конфигурации.

загрузка файла AppSyncjs из нашей консоли AppSync

Этот конфигурационный файл содержит информацию о клиенте AppSync, необходимую для создания нового клиента.

Верхний уровень приложения — это то, где мы будем настраивать интерфейс API AppSync с клиентом React Native.   Если вы ранее использовали Redux или React Apollo, это будет всем знакомо. Если вы этого не сделали, просто помните, что любой дочерний элемент Provider , в нашем случае ApolloProvider , будет иметь доступ к его заданным функциям.

Следующий код — наш новый файл App.js , который является основным компонентом, импортированным из нашей точки входа index.js.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
import React from ‘react’
import Tabs from ‘./src/Tabs’
 
import AWSAppSyncClient from «aws-appsync»;
import { Rehydrated } from ‘aws-appsync-react’;
import { ApolloProvider } from ‘react-apollo’;
 
import appSyncConfig from ‘./aws-exports’;
 
const client = new AWSAppSyncClient({
  url: appSyncConfig.graphqlEndpoint,
  region: appSyncConfig.region,
  auth: {
    type: appSyncConfig.authType,
    apiKey: appSyncConfig.apiKey,
  }
});
 
const WithProvider = () => (
  <ApolloProvider client={client}>
    <Rehydrated>
      <Tabs />
    </Rehydrated>
  </ApolloProvider>
);
 
export default WithProvider

В этом файле мы настраиваем новый клиент AppSync, используя комбинацию конструктора AWSAppSyncClient из aws-appsync а также конфигурацию в нашем файле aws-exports.js , который предоставляет URL-адрес GraphQL API, регион, тип аутентификации и ключ аутентификации API.

Затем мы обертываем нашу основную точку входа, файл Tabs.js, в котором будет храниться наша навигация по вкладкам, в ApolloProvider и передаем клиент AppSync в качестве клиентской поддержки. Мы также заключаем компонент Tabs в компонент aws-appsync-react который импортируем из aws-appsync-react . Это позволит убедиться, что мы прочитали из асинхронного хранилища и повторно выполнили регенерацию нашего кеша перед рендерингом интерфейса.

Теперь наше приложение сможет запрашивать данные с нашей конечной точки AppSync, а также выполнять мутации и подписки!

Основной точкой входа в приложение является навигация с вкладками, реализованная в файле Tabs.js с помощью React Navigation.

Здесь мы создали и экспортировали TabNavigator с двумя вкладками. Эти:

  1. Города . Этот компонент содержит список наших городов, и сам по себе он является компонентом навигатора. Этот компонент является навигатором, потому что мы хотим иметь возможность перемещаться к каждому отдельному городу и просматривать его местоположения.
  2. AddCity : этот компонент является формой, чтобы мы могли добавлять новые города.
Экран городов в приложении

Это приложение имеет только один повторно используемый компонент, настроенный TextInput . Поскольку мы будем дублировать этот стиль и функциональность снова и снова, мы решили сделать его собственным компонентом. Компонент ввода реализован в Input.js .

Основной вид приложения — это список городов, которые мы будем получать из GraphQL. Мы хотим иметь возможность перемещаться из каждого города в списке в подробный вид этого города, где мы можем добавлять местоположения.

Для этого мы делаем Cities.js своим собственным StackNavigator, а City.js — компонентом, к которому мы перемещаемся при выборе города. При нажатии на город в Cities , мы передаем его название и идентификатор в качестве реквизита для City .

В этом компоненте мы выбираем, используя запрос listCities , и мы также подписываемся на NewCitySubscription , чтобы при добавлении нового города, даже от другого клиента, мы обрабатывали эту подписку и обновляли наш массив городов. Запрос listCities делает массив городов доступным в нашем компоненте this.props.cities .

В этом компоненте нам передается город как реквизит от навигации (доступен как props.navigation.state.params.city ). Мы используем значение id города, чтобы получить список мест для выбранного города с listLocations запроса listLocations . Мы подписываемся на новые местоположения аналогично тому, как мы подписывались на новые города в Cities.js , используя подписку NewLocationSubscription . Мы также предоставляем функции optimisticResponse и update для добавления нового города.

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

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

Мутации, запросы и подписки являются основными функциями для интеграции с нашим API GraphQL. В нашем приложении эта функциональность реализована в файлах Cities.js , City.js и AddCity.js с помощью клиента AppSync.

Давайте подробнее рассмотрим, как в нашем коде реализованы мутации, запросы и подписки.

Сначала давайте посмотрим, как создать и экспортировать запрос GraphQL, который может взаимодействовать с запросом listCities в нашей схеме AppSync. Этот код содержится в файле src / reports / ListCities.js .

01
02
03
04
05
06
07
08
09
10
11
12
import gql from ‘graphql-tag’;
 
export default gql`
query listCities {
  listCities {
    items {
      name
      country
      id
    }
  }
}`

Затем мы импортируем этот запрос в файл Cities.js вместе с некоторыми помощниками из graphql react-apollo и graphql компонент, к которому мы хотели бы получить доступ к этим данным, используя compose и graphql из graphql react-apollo .

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
import { compose, graphql } from ‘react-apollo’
import ListCities from ‘./queries/ListCities’
 
class Cities extends React.Component {
  // class definition here
  // now have access to this.props.cities
}
 
export default compose(
  graphql(ListCities, {
      props: props => ({
        cities: props.data.listCities ?
      })
  })
)(CityList)

Теперь у нас есть доступ к массиву городов с нашего сервера GraphQL в качестве поддержки. Мы можем использовать this.props.cities для отображения массива городов, поступающего из GraphQL.

Чтобы создать мутацию, сначала нам нужно создать базовую мутацию GraphQL и экспортировать ее. Мы делаем это в файле src / mutations / CreateCity.js .

01
02
03
04
05
06
07
08
09
10
11
12
13
import gql from ‘graphql-tag’
 
export default gql`
  mutation addCity($name: String!, $country: String!, $id: ID!) {
    createCity(input: {
      name: $name, country: $country, id: $id
    }) {
      name
      country
      id
    }
  }
`

Теперь мы можем импортировать эту мутацию (вместе с помощниками Apollo) в файл AddCity.js и использовать ее в компоненте:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
import { compose, graphql } from ‘react-apollo’
import AddCityMutation from ‘./mutations/AddCity’
 
class AddCity extends React.Component {
  // class definition here
  // now have access to this.props.onAdd()
}
 
export default compose(
  graphql(AddCityMutation, {
    props: props => ({
      onAdd: city => props.mutate({
        variables: city
      })
    })
  })
)(AddCity)

Теперь у нас есть доступ к реквизиту onAdd , который мы передаем объекту, который мы хотим отправить мутации!

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

Во-первых, нам нужно создать мутацию и экспортировать ее, чтобы иметь доступ к ней в клиенте. Мы сохраняем это в файле src / subscriptionNewCitySubscription.js .

01
02
03
04
05
06
07
08
09
10
import gql from ‘graphql-tag’
 
export default gql`
subscription NewCitySub {
  onCreateCity {
    name
    country
    id
  }
}`;

Теперь мы можем импортировать и прикрепить подписку в Cities.js . Мы уже смотрели, как получить города из нашего API. Давайте теперь обновим эту функциональность, чтобы подписаться на новые изменения и обновлять массив городов при добавлении нового города.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
import AllCity from ‘./queries/AllCity’
import NewCitiesSubscription from ‘./subscriptions/NewCitySubscription’;
import { compose, graphql } from ‘react-apollo’
 
 
class Cities extends React.Component {
  componentWillMount(){
    this.props.subscribeToNewCities();
  }
  render() {
    // rest of component here
  }
}
 
export default compose(
  graphql(ListCities, {
    options: {
      fetchPolicy: ‘cache-and-network’
    },
    props: (props) => {
      return {
        cities: props.data.listCities ?
        subscribeToNewCities: params => {
          props.data.subscribeToMore({
            document: NewCitiesSubscription,
            updateQuery: (prev, { subscriptionData: { data : { onCreateCity } } }) => {
              return {
                …prev,
                listCities: {
                  __typename: ‘CityConnection’,
                  items: [onCreateCity, …prev.listCities.items.filter(city => city.id !== onCreateCity.id)]
                }
              }
            }
          })
        }
      }
    }
  })
)(Cities)

Мы добавляем новый реквизит с названием subscribeToNewCities , который мы вызываем в componentDidMount . В подписке мы передаем документ (определение подписки) и updateQuery чтобы описать, что мы хотим, чтобы происходило при обновлении.

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

Что если мы не хотим ждать, пока подписка вернет самые последние данные из нашего API, чтобы обновить наш интерфейс?

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

Мы можем сделать это легко, используя несколько методов и функций.

Давайте обновим нашу AddCityMutation следующим образом (вы можете просмотреть этот код в исходном файле AddCity.js ):

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
import { compose, graphql } from ‘react-apollo’
import AddCityMutation from ‘./mutations/AddCity’
 
class AddCity extends React.Component {
  // class definition here
  // now have access to this.props.onAdd()
}
 
export default compose(
  graphql(AddCityMutation, {
    props: props => ({
      onAdd: city => props.mutate({
        variables: city,
        optimisticResponse: {
          __typename: ‘Mutation’,
          createCity: { …city, __typename: ‘City’ }
        },
        update: (proxy, { data: { createCity } }) => {
          const data = proxy.readQuery({ query: ListCities });
          data.listCities.items.unshift(createCity);
          proxy.writeQuery({ query: ListCities, data });
        }
      })
    })
  })
)(AddCity)

Здесь мы добавили два новых свойства в объект аргумента функции mutate:

  1. optimisticResponse определяет новый ответ, который вы хотели бы получить в функции обновления.
  2. update принимает два аргумента: прокси (который позволяет читать из кэша) и данные, которые вы хотели бы использовать для обновления. Мы читаем текущий кеш ( proxy.readQuery ), добавляем наш новый элемент в массив элементов, а затем записываем обратно в кеш, который обновил наш пользовательский интерфейс.

GraphQL становится все более популярным. Большая часть сложности, связанной с GraphQL, связана с управлением серверной частью и уровнем API. Однако такие инструменты, как AppSync, отвлекают эту сложность, освобождая разработчиков от необходимости тратить большую часть своего времени на настройку и работу на сервере.

Я с нетерпением жду новых инноваций в этом пространстве и не могу дождаться, чтобы увидеть, что еще мы увидим в 2018 году!

Если вы заинтересованы в использовании AppSync вместе с Serverless Framework, ознакомьтесь с этим прекрасным введением в их совместное использование .

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

Если вы хотите внести свой вклад в этот проект, вы можете подключиться к нашему репозиторию GitHub . Если у вас есть какие-либо идеи, не стесняйтесь, присылайте нам пиар, или используйте это приложение как стартовый для вашего следующего проекта React Native GraphQL!

А пока, посмотрите некоторые другие наши учебники по React Native здесь на Envato Tuts +!

  • Мобильная разработка
    Инструменты для React Native Development
    Верн Анчета
  • React Native
    Практические примеры анимации в React Native
    Верн Анчета
  • React Native
    Начните с React Native Layouts
    Верн Анчета
  • реагировать
    10 Реагируйте на собственные приложения для использования, изучения и применения
    Эрик Дай