# GraphQLクライアント

  • ApolloClientを使ってGraphQLクライアントを作ります
  • 今回はReactを使ったSPAで前節で作成したGraphQLサーバにアクセスします

# ゴール

  • ApolloClientのセットアップの仕方を知る
  • useQuery/useMutationの使い方や動きを知る

# セットアップ

  • Reactアプリの雛形作成します
npm i -g create-react-app
create-react-app apollo-client-sample
cd apollo-client-sample
1
2
3
  • ApolloClientとGraphQLのライブラリを追加します
yarn add @apollo/client graphql
1
  • 以下のコマンドで起動することを確認しておきます
yarn start
1

# ファイルの作成

  • まずはApolloClientのセットアップから入ります
    • src/graphql/client.jsを作成して以下の内容を記述してください
    • urlには前節で作成したGraphQLサーバのURLを書いておきます
import { ApolloClient, HttpLink, InMemoryCache } from '@apollo/client';

export default new ApolloClient({
  cache: new InMemoryCache(),
  link: new HttpLink({
    uri: 'http://localhost:4000/graphql',
  }),
});
1
2
3
4
5
6
7
8
  • 次に、実行するQueryやMutationを定義します(まずはQueryだけ)
    • Playgroundの左側のフィールドに入力していた内容を定義するようなイメージです
    • src/graphql/schema.jsを作成して以下の内容を記述してください
import { gql } from '@apollo/client';

export const GET_TEAMS = gql`
  query Teams {
    teams {
      id
      name
      foundingDate
      homeStadium
      players {
        no
        name
        position
      }
    }
  }
`;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
  • 次にサーバから取得した内容を表示するためのコンポーネントを作成します
    • src/components/Teams.jsを作成して以下の内容を記述してください
    • useQueryが実行されるとQueryが実行されます(=サーバへのアクセスが走る)
      • 実行中はloadingがtrue, 完了するとloadingがfalseになり結果に応じてdataとerrorに値が入ります
      • loadingやdataなどの値が変化すると自動的にTeams関数が再実行されるので非常に便利です
    • dataの構造はconsoleに出力するようにしてるのでDevtoolsから確認してみてください
import React from 'react';
import { useQuery } from '@apollo/client';
import { GET_TEAMS } from '../graphql/schema';

function Teams() {
  // Queryを実行する
  const { loading, error, data } = useQuery(GET_TEAMS);
  console.log({ loading, error, data });

  if (loading) return 'Loading...';
  if (error) return `Error! ${error.message}`;

  return (
    <div>
      {data.teams.map(team => (
        <dl>
          <dt>ID</dt>
          <dd>{team.id}</dd>
          <dt>チーム名</dt>
          <dd>{team.name}</dd>
          <dt>創設日</dt>
          <dd>{team.foundingDate}</dd>
          <dt>ホーム球場</dt>
          <dd>{team.homeStadium}</dd>
          <dt>所属選手</dt>
          <dd>
            {team.players.map(player => (
              <dl>
                <dt>背番号</dt>
                <dd>{player.no}</dd>
                <dt>名前</dt>
                <dd>{player.name}</dd>
                <dt>守備位置</dt>
                <dd>{player.position}</dd>
              </dl>
            ))}
          </dd>
        </dl>
      ))}
    </div>
  );
}

export default Teams;
1
2
3
4
5
6
7
8
9
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
41
42
43
44
  • 最後にReactアプリのエントリーポイントにこれまで作ったファイルを適用します
    • src/App.jsを以下の内容に修正してください
import React from 'react';
import { ApolloProvider } from '@apollo/client';

import client from './graphql/client';
import Teams from './components/Teams';

function App() {
  return (
    <ApolloProvider client={client}>
      <Teams />
    </ApolloProvider>
  );
}

export default App;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

# 動作確認

  • ここまでできたらブラウザで確認してみます

    • 先程アプリを起動したままの人はブラウザが自動でリロードされてすでに画面が更新されているはずです
    • 停止している人は以下のコマンドで起動し確認してみてください
    yarn start
    
    1
  • このような画面が表示されていればOKです teams

# Mutationの実行を追加

  • 取得系のQueryが実行できたので今度は操作系のMutationも追加する
    • 前節で作成した選手を追加するMutationを追加してみます
  • src/graphql/schema.jsにmutationの定義を追加してください
// 省略

// 一番下にこれを追加する
export const ADD_PLAYER = gql`
  mutation AddPlayer(
    $name: String!
    $no: String!
    $position: String!
    $teamId: String!
  ) {
    addPlayer(name: $name, no: $no, position: $position, teamId: $teamId) {
      id
      no
      name
      position
    }
  }
`;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
  • 選手を追加するための入力フォームコンポーネントを作成します
    • src/components/AddPlayer.jsを作成して以下の内容を記述してください
      • useStateonChangeの辺りはReactの書き方の話で今回の本質からそれるので説明は割愛します
      • onSubmit関数の中にある{ variables: form }の部分で入力内容をセットできてるんだなくらいに思っておいてください
import React, { useState } from 'react';
import { useMutation } from '@apollo/client';
import { ADD_PLAYER } from '../graphql/schema';

function AddPlayer() {
  const [form, setForm] = useState({});
  // addPlayer関数を実行するとMutationが実行される
  const [addPlayer, { data }] = useMutation(ADD_PLAYER);
  console.log({ data });

  const onChange = e => {
    setForm({ ...form, [e.target.name]: e.target.value });
  };

  const onSubmit = e => {
    e.preventDefault();
    // Submitボタンを押した時にMutationを実行する
    // 引数に入力内容をセットしている
    addPlayer({ variables: form });
  };

  return (
    <form onSubmit={onSubmit}>
      <label htmlFor="name">
        名前 <input name="name" id="name" onChange={onChange} />
      </label>
      <label htmlFor="no">
        背番号
        <input name="no" id="no" onChange={onChange} />
      </label>
      <label htmlFor="position">
        守備位置
        <input name="position" id="position" onChange={onChange} />
      </label>
      <label htmlFor="teamId">
        チームID
        <input name="teamId" id="teamId" onChange={onChange} />
      </label>
      <button>作成</button>
    </form>
  );
}

export default AddPlayer;
1
2
3
4
5
6
7
8
9
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
41
42
43
44
  • src/App.jsにAddPlayerを反映します
import React from 'react';
import { ApolloProvider } from '@apollo/client';

import client from './graphql/client';
import Teams from './components/Teams';
import AddPlayer from './components/AddPlayer';

function App() {
  return (
    <ApolloProvider client={client}>
      <AddPlayer />
      <Teams />
    </ApolloProvider>
  );
}

export default App;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
  • ブラウザにアクセスして動作確認してみます
  • うまくいっていれば以下のような画面が出ているはずです form
  • フォームに値を入力して作成してみてください post
  • リロードすると一覧に反映されているはずです! teams

# 更新ボタンの追加

  • 画面をリロードしないと更新を確認できないのはいけてないので更新ボタンを作ります
    • GraphQLのSubscriptionという機能を使うと自動更新できますが資料作成間に合わず・・・いつかアップデートします
  • src/components/Teams.jsに更新ボタンの追加します
    • useQueryからrefetchというQueryを再実行する関数がとれるのでそれを活用します
import React from 'react';
import { useQuery } from '@apollo/client';
import { GET_TEAMS } from '../graphql/schema';

function Teams() {
  // ↓refetchを追加
  const { loading, error, data, refetch } = useQuery(GET_TEAMS);
  console.log({ loading, error, data });

  if (loading) return 'Loading...';
  if (error) return `Error! ${error.message}`;

  return (
    <div>
      {/* ↓ボタンを追加 */}
      <button onClick={() => refetch()}>更新</button>
      {/* 省略 */}
    </div>
  );
}

export default Teams;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
  • これで選手を作成した後に更新ボタンを押すことで一覧が更新されるようになりました
Last Updated: 2020/03/19