ApolloClient × Custom HookでGrahQLサーバのデータを取得する

## はじめに

GrahQLをサポートするApolloClientと、React CustomHookを使って、サーバ上のデータを取得する方法を紹介します。

## サンプルイメージ
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/502798/214810ed-3d3e-8f0d-66ef-49b4342a7697.png)

今回はバックエンドGrahQLサーバとして、SpaceXが提供しているGrahQL APIを利用します。

https://api.spacex.land/graphql/

## 前提
create-react-app等で、既にReactプロジェクトが作成されていること。

## 手順
1. ライブラリインストール
1. AppolloClientの設定
1. useQueryでデータ取得

### ライブラリインストール
下記を実行し、ApolloClientをインストールする。

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

###  AppolloClientの設定

上位のコンポーネント(index.jsとか)で必要なオブジェクトをimportする。

```index.js
import {
  ApolloClient,
  InMemoryCache,
  ApolloProvider
} from "@apollo/client";
```

ApolloClientをSpaceXのGraphQLサーバと接続するように設定し、インスタンス化する。
(ここで指定する`cache`は、ApolloClientを通じてやり取りしたデータのキャッシュを保持するオブジェクトのこと。`cache`内のデータはApolloProviderにラップされているコンポーネントならどこからでも参照できます。)

```index.js
const client = new ApolloClient({
  uri: 'https://api.spacex.land/graphql/',
  cache: new InMemoryCache()
});
```

その後、下記のようにGraphQLサーバと接続するコンポーネントを`ApolloProvider`でラップしてあげる。(ApolloProvider配下にいる場合のみ、引数として受け渡された`client`を使ってリクエストを投げることができます。)

```index.js
ReactDOM.render(
  <React.StrictMode>
    <ApolloProvider client={client}>
      <App />
    </ApolloProvider>
  </React.StrictMode>,
  document.getElementById('root')
);

```

ここまでの`index.js`全体

```index.js
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import reportWebVitals from './reportWebVitals';
import {
  ApolloClient,
  InMemoryCache,
  ApolloProvider,
} from "@apollo/client";

const client = new ApolloClient({
  uri: 'https://api.spacex.land/graphql/',
  cache: new InMemoryCache()
})

ReactDOM.render(
  <React.StrictMode>
    <ApolloProvider client={client}>
    <App />
    </ApolloProvider>
  </React.StrictMode>,
  document.getElementById('root')
);

```

### useQueryでデータ取得
ここから、データ取得のロジックを作成していきます。

下記を実行し、GrahQLサーバへのリクエストクエリ発行に必要なオブジェクトをimportします。

```App.js
import {
  useQuery,
  gql
} from "@apollo/client";
```

#### クエリ作成

`gql`を利用して、クエリを作成します。
下記クエリはSpaceX GrahQL APIから、指定した名前の船情報(船名・画像・id)を取得するクエリです。

```App.js
  const GET_SHIPS = gql`
  query GetShips() {
    ships(find: {name: 'American Champion'}) {
      name
      image
      id
    }
  }
  `;
```

#### クエリのリクエスト

作成したクエリを`useQuery`を使って、リクエストします。

`useQuery`は`{ loading, error, data }`を返却します。
ロード中であれば`loading`、
エラーが発生すれば`error`、
正常にデータが取得出来たら`data`を返却します。

上記以外にも`useQuery`はいろいろ便利なもの返却するので、下記参照してみてください。
https://www.apollographql.com/docs/react/data/queries/#usequery-api

```App.js
  const shipName = '';

  const { loading, error, data } = useQuery(GET_SHIPS ,{
    variables: {name: shipName},
  });

  if (loading) return 'loading';
  if (error) return 'error';

  return {
    shipData:data.ships
  }
```

上記ロジックをカスタムフックにまとめたコードが下記になります。

```App.js
import './App.css';
import {
  useQuery,
  gql
} from "@apollo/client";
import React from 'react';

function useExchangeRates() {
  const GET_SHIPS = gql`
  query GetShips() {
    ships(find: {name: "American Champion"}) {
      name
      image
      id
    }
  }
  `;

  const shipName = 'American Champion';

  const { loading, error, data } = useQuery(GET_SHIPS);

  if (loading) return 'loading';
  if (error) return 'error';

  return {
    shipData:data.ships
  }
}

function ShowshipData(props){
    if(!props.shipData){
      return(<p>no data</p>);
    }else if (props.shipData === 'loading'){
      return(<p>loading</p>);
    }else if(props.shipData === 'error'){
      return(<p>loading</p>);
    }
  
    return props.shipData.map(({ name, image }) => (
      <div>
        <p>{name}</p>
        <img src={image} alt=''/>
      </div>
    ));
}

function App() {
  console.log("App rerendered");
  const {shipData} = useExchangeRates();

  return (
    <div className="App">
      <div>
        <ShowshipData shipData={shipData}/>
      </div>
    </div>
  );
}

export default App;
```

以上が、GrahQLサーバのデータを取得する方法になります。

## おまけ

### リクエストの引数(variables)を変えて、再度データ取得を行う
上記コード例の場合、クエリの`variables`が固定値(American Champion)です。

任意のvariablesを指定かつ、一度実行したクエリを手軽に再実行する方法を紹介します。

### クエリ作成

クエリを下記の通り修正します。
`GetShips`クエリが、引数として`name`を受け取る宣言をしてます。

```App.js
function useExchangeRates() {
  const GET_SHIPS = gql`
  query GetShips($name: String!) {
    ships(find: {name: $name}) {
      name
      image
      id
    }
  }
  `;
```

#### クエリのリクエスト

`useQuery`の第二引数で指定している`variables`がクエリの引数になります。(今回で言うと船名)


```App.js
  const shipName = 'other ship';

  const { loading, error, data, refetch } = useQuery(GET_SHIPS ,{
    variables: {name: shipName},
  });
```

上記実行すると、指定した船名のデータを取得することができます。

補足：
上記のように`useQuery`の返り値として、`refetch`を取得しておけば、次回データ取得の際に下記のように利用することができます。

```App.js
refetch({
    variables: {name: shipName}
  })
```

## 参考

https://www.apollographql.com/docs/react/caching/cache-interaction/

https://api.spacex.land/graphql/