Как использовать совершенно новые компоненты запроса Apollo для управления локальным состоянием

kak ispolzovat sovershenno novye komponenty zaprosa apollo dlya upravleniya lokalnym

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

Введение

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

Поэтому выбор клиента Apollo для обработки моего локального состояния и моих удаленных данных кажется непростым. Зачем сталкиваться с шаблоном и идиомами Redux, когда я уже настроил Apollo/GraphQL для получения данных с моего сервера?

Хотя эта статья будет иметь дело с настройкой Apollo для обработки локальных состояний, она не будет вступлением в технологию. (Этот законный учебник howtographql является хорошим началом для этого).

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

Настройка

Мы начнем с клонирования соответствующего репо отсюда. Этот репозиторий содержит простой сайт с реагированием с боковой панелью, заголовком и телом. Он достаточно статический по своей сути, без динамического содержимого (пока). В конце учебника Apollo будет управлять состоянием веб-сайта. Нажатие элемента на боковой панели изменит состояние веб-сайта, который, в свою очередь, обновит заголовок для отображения новых данных.

Если проверить package.json Вы увидите, что у нас только основные, а также некоторые дополнительные пакеты, касающиеся настройки посылок.

После клонирования репо запустите стандартные команды в интерфейсе командной строки.

> yarn
> yarn dev

Чтобы установить все ваши пакеты и создать локальный сервер, перейдите на localhost:1234 и, надеемся, вы увидите демонстрационный веб-сайт во всей его красоте. Сейчас он статический, поэтому щелчок вокруг ничего не даст.

Прежде всего, что мы хотим сделать, это включить Apollo в наш проект, поэтому установите эти пакеты. apollo-client позволяет нам настроить наш экземпляр Apollo, и react-apollo это драйвер, который позволяет нам интегрировать его в наше приложение React. Из-за проблемы с посылкой (я думаю) нам также нужно будет установить graphql.

> yarn add apollo-client react-apollo graphql

Создайте новый каталог src/apolloоткройте an index.js файл и добавьте следующее:

import ApolloClient from ‘apollo-client’;
export const client = new ApolloClient({});

Это инициализирует наш клиент Apollo, который мы затем используем для обертывания нашей программы React, добавив следующее внутрь нашего src/index.js файл.

import { ApolloProvider } from ‘react-apollo’;
import { client } from ‘./apollo’;

const WrappedApp = (
  <ApolloProvider client={client} >
    <App />
  </ApolloProvider>
);

ReactDOM.render(WrappedApp, document.getElementById(‘root’));
// Don’t be a sap. Wrap your app.

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

> yarn add apollo-link apollo-cache-inmemory apollo-link-state

Предыдущая строка добавляет новые зависимости Apollo к нашему приложению, а следующий код устраняет ошибки консоли, которые мы получали. Итак, вернитесь к apollo/index.js и обновите его, чтобы файл выглядел так:

import ApolloClient from ‘apollo-client’;
import { InMemoryCache } from ‘apollo-cache-inmemory’;
import { ApolloLink } from ‘apollo-link’;
import { withClientState } from ‘apollo-link-state’;

const cache = new InMemoryCache();
const stateLink = withClientState({
  cache
});

export const client = new ApolloClient({
  cache,
  link: ApolloLink.from([
    stateLink,
  ]),
})

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

Вы можете видеть, что мы также добавили link объект нашего клиента. The ApolloLink.from()Метод позволяет нам модульно настроить способ отправки наших запросов через HTTP. Мы можем использовать это для обработки ошибок и авторизации, а также для предоставления доступа к нашему серверу. Мы не собираемся делать ничего из этого в учебнике, но мы настроим наше клиентское состояние здесь. Так что творим const stateLink выше и передайте в наш кэш. Позже мы добавим сюда наше состояние по умолчанию и резольверы.

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

Внутри каталога Apollo создайте новый каталог под названием defaults и добавьте ан index.js внутри него. Файл будет содержать следующее:

export default {
  apolloClientDemo: {
    __typename: ‘ApolloClientDemo’,
    currentPageName: ‘Apollo Demo’,
  }
}

Мы создаем объект, действующий как состояние по умолчанию нашего сайта. apolloClientDemo – это имя структуры данных, к которой мы хотим получить доступ, когда делаем запросы. The __typename является обязательным идентификатором, использующим наш кэш, а currentPageName – это конкретный элемент данных, который наш заголовок будет использовать для отображения названия текущей страницы – вы уже догадались.

Нам нужно будет добавить это к нашим apollo/index.js файл:

import defaults from ‘./defaults’;

const stateLink = withClientState({
  cache,
  defaults,
});

Давайте немного проясним это. import и default оба ключевых слова, связанных с модулями импорта, но по стечению обстоятельств имя объекта, из которого мы экспортируем ./defaults также называется defaults (поэтому не думайте, что я использую import/export неправильно). Рассматривайте эту строчку импорта так, будто это был просто обычный импортированный старый импорт.

После этого давайте создадим запрос!

Как сделать запрос

Добавьте следующий пакет в свой проект:

> yarn add graphql-tag

и создать новый каталог src/graphql. Там создайте два новых файла: index.js и getPageName.js. В каталоге GraphQL будут размещены все запросы и мутации. Мы создадим наш запрос в getPageName.js написав следующее:

import gql from ‘graphql-tag’;

export const getPageNameQuery = gql`
  query {
    apolloClientDemo @client {
      currentPageName
    }
  }
`;

export const getPageNameOptions = ({
  props: ({ data: { apolloClientDemo } }) => ({
    apolloClientDemo
  })
});

Итак, мы экспортируем две переменные, запрос и параметры. Если вы использовали GraphQL, запрос будет выглядеть знакомым. Мы спрашиваем структуру данных apolloClientDemo, возвращая не что иное, как currentPageName. Вы заметите, что мы добавили @client директиву по нашему запросу. Это сообщает Apollo, чтобы спросить наше местное состояние вместо того, чтобы посылать запрос в бэкенд.

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

props: {
  currentPageName: ‘Apollo Demo’,
}
// and not this
props: {
  data: {
    apolloClientDemo: {
      currentPageName: ‘Apollo Demo’,
    }
  }
}

Перейдите к graphql/index.js файл и экспортируйте запрос следующим образом:

export { getPageNameQuery, getPageNameOptions } from ‘./getPageName’;

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

Добавьте в свой Header.js:

import React from 'react';
import { Query } from 'react-apollo';
import { getPageNameQuery } from '../graphql';

const Header = () => (
    <Query query={getPageNameQuery}>
        {({ loading, error, data }) => {
            if (error) return <h1>Error...</h1>;
            if (loading || !data) return <h1>Loading...</h1>;

            return <h1>{data.apolloClientDemo.currentPageName}</h1>
        }}
    </Query>
);

export default Header;

Это наше первое использование нового компонента запроса Apollo, добавленного в версии 2.1. Мы импортируем Query от react-apollo и используем его для обертывания остального компонента. Затем мы передаем getPageNameQuery как значение параметра запроса. Когда наш компонент воспроизводится, он запускает запрос и предоставляет остальному компоненту доступ к данным, которые мы деструктурируем, чтобы получить доступ к загрузке, ошибкам и данным.

Компонент запроса использует шаблон render props, чтобы предоставить остальному компоненту доступ к информации, полученной из запроса. Если вы использовали React Context API в версии 16.3, то вы видели этот синтаксис раньше. В противном случае стоит ознакомиться с официальными документами React здесь, поскольку шаблон Render Props становится все более популярным.

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

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

0*OHpQBcsRCsX5Wk_b
Освежающее изображение для разбиения текста. Джефф Шелдон

Мутации

С мутациями все становится сложнее. Мы больше не просто получаем данные из магазина Apollo, но и обновляем их. Архитектура мутации такова:

> Вser нажимает элемент боковой панели

> Sends переменная к мутации

> Fires мутация с переменной

> Гets отправлено в инстанцию ​​Аполлона

> Finds соответствующий резольвер

> Applэто логика в магазин Apollo

> Seданные nds назад к заголовку

Если это трудно запомнить, воспользуйтесь этой удобной мнемосхемой, созданной с помощью генератора мнемона: Urban Senile Fauns Roped Faithless Aslan Solemnly. (легко…)

Начните с создания файла graphql/updatePageName.js.

import gql from ‘graphql-tag’;

export const updatePageName = gql`
  mutation updatePageName($name: String!) {
    updatePageName(name: $name) @client {
      currentPageName
    }
  }
`;

и экспортируйте его так же, как мы сделали с запросом.

export { updatePageNameMutation } from ‘./updatePageName’;

Вы заметите несколько отличий по мутации. Сначала мы изменили ключевое слово по запросу на мутацию. Это позволяет GraphQL знать тип действия, которое мы выполняем. Мы также определяем имя запроса и добавляем типы переменных, которые мы передаем. Здесь мы указываем имя разделителя, используемого для внесения изменений. Мы также проходим через переменную и добавляем @client директива.

В отличие от запроса, мы не можем просто добавить мутацию к нашему представлению и ожидать, что что-нибудь произойдет. Нам придется вернуться в наш каталог Apollo и добавить наши резольверы. Создайте новый каталог apollo/resolversи файлы index.js и updatePageName.js. Внутри updatePageName.jsдобавить следующее:

import gql from ‘graphql-tag’;

export default (_, { name }, { cache }) => {
  const query = gql`
    query GetPageName {
      apolloClientDemo @client {
        currentPageName
      }
    }
  `;
  
  const previousState = cache.readQuery({ query });
  
  const data = {
    apolloClientDemo: {
      …previousState.apolloClientDemo,
      currentPageName: name,
    },
  };
  
  cache.writeQuery({
    query,
    data,
  });
  
  return null;
};

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

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

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

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

Тем временем в resolvers/index.js файл…

import updatePageName from ‘updatePageName’;

export default {
  Mutation: {
    updatePageName,
  }
};

Это форма объекта, которую ожидает Apollo, когда мы передаем наши резольверы в stateLink назад apollo/index.js:

import resolvers from ‘./resolvers’;

const stateLink from = withClientState({
  cache,
  defaults,
  resolvers,
});

Все, что осталось сделать, это добавить мутацию к нашему компоненту боковой панели.

// previous imports
import { Mutation } from ‘react-apollo’;
import { updatePageNameMutation } from ‘../graphql’;

class Sidebar extends React.Component {
  render() {
    return (
      <Mutation mutation={updatePageNameMutation}>
        {updatePageName => (
          // outer div elements
          <li className=“sidebar-item” onClick={() => updatePageName({ variables: { name: ‘React’} })}>React</li>
          // other list items and outer div elements
        )}
      </Mutation>
    );
  }
}

export default Sidebar;

Как и наш файл резольвера, в этом файле многое происходит, но он новый. Мы импортируем свои Mutation компонент от react-apolloоберните его вокруг нашего компонента и передайте updatePageNameMutation внутри mutation опора.

Теперь компонент имеет доступ к updatePageName метод, запускающий мутацию всякий раз, когда она вызывается. Мы делаем это, добавляя метод как обработчик к <li> свойство onClick. Метод ожидает получения объекта, содержащего переменные в качестве параметра, поэтому введите имя, до которого необходимо обновить заголовок. Если все работает, вы сможете запустить сервер разработчиков и нажать элементы боковой панели, которые затем должны изменить наш заголовок.

Подведению

Ура! Надеюсь, все обошлось. Если вы застряли, посмотрите репо здесь. Он содержит весь готовый код. Если вы планируете использовать локальное управление состоянием в следующем приложении React, вы можете разделить это репо и продолжить с него. Если вы заинтересованы в том, чтобы об этой статье/теме говорили на встрече или конференции, отправьте мне сообщение!

В этом учебнике я хотел рассказать о многом другом, например, асинхронных резольверах (думайте, Redux thunk), проверке типов/создании схемы и обновлении мутации. Так кто знает… возможно, скоро я заброшу еще одну статью.

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

Спасибо, что прочли!

Если вы заинтересованы в том, чтобы принять меня на конференцию, встречу или в качестве выступающего гостя для любого участия, вы можете написать мне в Twitter!

Как использовать совершенно новые компоненты запроса Apollo для управления локальным состоянием

Добавьте к своему веб-приложению нотку напряженности с помощью React.lazy()

Не нужно ждать праздника, начните украшать сейчас

Управление местным штатом с помощью Аполлона и компонентов высшего порядка

Игра с выпивкой React Conference

Разработайте и разверните собственное приложение React monorepo менее чем за 2 часа, используя Lerna, Travis и Now

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *