React + TypeScript: Apollo ClientのGraphQLクエリを使ってみる

[Apollo Client](https://www.apollographql.com/docs/react/)はReactで使える状態管理ライブラリです。ローカルとリモートのデータをGraphQLで扱えます。本稿でつくるのは、Apollo Client公式の「[Get started with Apollo Client](https://www.apollographql.com/docs/react/get-started/)」で紹介されている作例です。ただし、TypeScriptを採り入れ、アプリケーションは簡単にモジュール分けしました。

# Apollo Clientアプリケーションをつくる準備
Reactアプリケーションのひな形は、[Create React App](https://ja.reactjs.org/docs/create-a-new-react-app.html#create-react-app)でつくることにします。オプションとして`--template typescript`を加えれば、TypeScriptの環境が簡単に加わえられて便利です(「[Create React AppでTypeScriptが加わったひな形アプリケーションをつくる](http://www.fumiononaka.com/Business/html5/FN2006001.html#sec01)」参照)。

では、Apollo Clientを[インストール](https://www.apollographql.com/docs/react/get-started/#1-setup)しましょう。GraphQLも、依存があるのでインストールに加えてください(クエリを使わなくても必要です)。

```terminal
npm install @apollo/client graphql
```


# `ApolloClient`を初期化する
まず、[`ApolloClient`](https://www.apollographql.com/docs/react/api/core/ApolloClient/#the-apolloclient-constructor)のインスタンスをつくります。コンストラクタに渡すのはオプションオブジェクトで、ふたつの設定を加えました。`uri`はGraphQLサーバーのURLの文字列です。`cache`はApollo Clientが読み込んだクエリ結果の保存先として必須で、多くの場合[`InMemoryCache`](https://www.apollographql.com/docs/react/api/cache/InMemoryCache/)インスタンスが定められます。

* `uri: string` - Apollo Clientが通信するGraphQLのエンドポイントURI。
* `cache: ApolloCache` - クエリの結果をローカルに保存するためにApollo Clientが用いるべきキャッシュ(必須)。 `@apollo/client`パッケージの提供する`InMemoryCache`が推奨。

Reactのページへのデータの差し込みはあとにして、クエリを送ってみましょう(コード001)。`ApolloClient`インスタンスに対して呼び出すのは`query()`メソッドです。引数には、`gql`テンプレートリテラルで包んだ、クエリ文字列を渡してください。

#### コード001■`ApolloClient`でクエリを送る
```react:src/App.tsx
import { useEffect } from 'react';
import {
	ApolloClient,
	InMemoryCache,
	gql,
} from '@apollo/client';

const client = new ApolloClient({
	uri: 'https://48p1r2roz4.sse.codesandbox.io',
	cache: new InMemoryCache(),
});
function App() {
	useEffect(() => {
		client
			.query({
				query: gql`
					query GetRates {
						rates(currency: "USD") {
							currency
						}
					}
				`,
			})
			.then((result) => console.log(result));
	}, []);
	return (
		<div>
			<h1>Apollo Client</h1>
		</div>
	);
}
export default App;
```

コードを実行すると、コンソールにクエリ結果のオブジェクトが示されるはずです(図001)。動きは、CodeSandboxに公開した以下のサンプル001でお確かめください[^1]。

[^1]: なお、CodeSandboxでは、graphqlのバージョンを16以上にすると、[TypeError](https://qiita.com/FumioNonaka/items/1329667b64f9a8b9e153#%E3%82%B3%E3%83%BC%E3%83%89002apollo-client%E3%82%92%E7%94%A8%E3%81%84%E3%81%9F%E3%82%AB%E3%82%B9%E3%82%BF%E3%83%A0%E3%83%95%E3%83%83%E3%82%AF%E3%81%AE%E3%83%A2%E3%82%B8%E3%83%A5%E3%83%BC%E3%83%AB)が出てしまうようです。

#### 図001■コンソールに出力されたクエリの結果
![2203002-001.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/128949/771da40c-9cd6-f018-8c86-ecd461f86765.png)

#### サンプル001■React + TypeScript: Using GraphQL queries with Apollo Client 01
https://codesandbox.io/s/react-typescript-using-graphql-queries-with-apollo-client-01-5mpkom


# `ApolloProvider`でReactのコンテクストにつなぐ
[`ApolloProvider`](https://www.apollographql.com/docs/react/api/react/hooks/#the-apolloprovider-component)コンポーネントを使うと、Apollo ClientをReactのコンテクストにつなげられます。仕組みはReactの[`Context.Provider`](https://ja.reactjs.org/docs/context.html#contextprovider)と同じです。包まれたツリーの中のどのコンポーネントからでも、Apollo Clientのデータにアクセスできます。GraphQLのデータを用いる大もとのコンポーネントを`ApolloProvider`に含めてください。プロパティ`client`に与えるのは、`ApolloClient`インスタンスです。


```react:src/App.tsx
// import { useEffect } from 'react';
// import { ApolloClient, InMemoryCache, gql } from '@apollo/client';
import {

	ApolloProvider,

} from '@apollo/client';

function App() {
	/* useEffect(() => {

	}, []); */
	return (
		<ApolloProvider client={client}>
			<div>

			</div>
		</ApolloProvider>
	);
}
```


# `useQuery`フックでデータを読み込む
`ApolloProvider`でReactのコンテクストにつなげたら、データをクエリで要求してページに読み込めます。用いるフックが`useQuery`です。GraphQLのデータをReactと共有します。クエリは、ルートモジュール(`src/App.tsx`)に変数(`ExchangeRates`)で定めておきましょう。求めるプロパティとして、通貨(`currency`)のほかに為替レート(`rate`)を加えました。このあと定める子コンポーネント(`ExchangeRates`)のプロパティ(`exchangeRates`)にクエリを加えます。

```react:src/App.tsx
import { ExchangeRates } from './ExchangeRates';

const EXCHANGE_RATES = gql`
	query GetExchangeRates {
		rates(currency: "USD") {
			currency
			rate
		}
	}
`;
function App() {
	return (
		<ApolloProvider client={client}>
			<div>

				<ExchangeRates exchangeRates={EXCHANGE_RATES} />
			</div>
		</ApolloProvider>
	);
}
```

[`useQuery`](https://www.apollographql.com/docs/react/api/react/hooks/#usequery)フックに渡したクエリ(`DocumentNode`型)は、コンポーネントが描画されるたびに実行され、返されるのは新たな[結果](https://www.apollographql.com/docs/react/api/react/hooks/#result)のオブジェクトです。為替レートを表示する子のモジュール(`src/ExchangeRates.tsx`)は、3つのプロパティを取り出しています(コード002)。

* `loading: boolean` - ロード中かどうかのブール値。`true`のときは、まだクエリの結果が返されていない。
* `error: ApolloError` - クエリにエラーが起こったことを示すオブジェクト。
* `data: TData` - GraphQLクエリが正しく実行されたときに得られる結果のデータ。

コードを実行すると、少しの間「Loading...」のテキストが示され、そのあと為替レートの一覧が表示されるでしょう。クエリの状態に応じて、コンポーネントがリアクティブに再描画されているということです。書き改めたルートモジュール(`src/App.tsx`)のコードも併せてまとめておきました。動きはCodeSandboxに公開した以下のサンプル002でお確かめください。

#### コード002■useQueryフックで読み込んだデータをページに表示する。
```react:src/ExchangeRates.tsx
import { VFC } from 'react';
import {
	DocumentNode,
	useQuery,
} from '@apollo/client';

type Props = {
	exchangeRates: DocumentNode;
};
export const ExchangeRates: VFC<Props> = ({ exchangeRates }) => {
	const { loading, error, data } = useQuery(exchangeRates);
	if (loading) return <p>Loading...</p>;
	if (error) return <p>Error :(</p>;
	return data.rates.map(
		({ currency, rate }: { currency: string; rate: string }) => (
			<div key={currency}>
				<p>
					{currency}: {rate}
				</p>
			</div>
		)
	);
};
```

```react:src/App.tsx
import {
	ApolloClient,
	ApolloProvider,
	InMemoryCache,
	gql
} from '@apollo/client';
import { ExchangeRates } from './ExchangeRates';

const client = new ApolloClient({
	uri: 'https://48p1r2roz4.sse.codesandbox.io',
	cache: new InMemoryCache(),
});
const EXCHANGE_RATES = gql`
	query GetExchangeRates {
		rates(currency: "USD") {
			currency
			rate
		}
	}
`;
function App() {
	return (
		<ApolloProvider client={client}>
			<div>
				<h1>Apollo Client</h1>
				<ExchangeRates exchangeRates={EXCHANGE_RATES} />
			</div>
		</ApolloProvider>
	);
}
export default App;
```

#### サンプル002■React + TypeScript: Using GraphQL queries with Apollo Client 02
https://codesandbox.io/s/react-typescript-using-graphql-queries-with-apollo-client-02-nbiv08