22
13

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

【React】ApolloClientを使ってGraphQLサーバにアクセスする

Last updated at Posted at 2020-03-27

概要

  • ReactからGraphQLサーバにアクセスするためのClientライブラリとしてApolloClientがあります
  • 数年前にさわったことがありましたがHooks対応してかなり使いやすくなっていたのでセットアップとQuery/Mutationの叩き方を簡単に紹介します
  • ※ApolloClientは執筆日時点(2020/3/28)でbeta版であるv3系を使っています

事前準備

  • Reactの追加
yarn add react react-dom
  • index.htmlの作成
index.html
<div id="root"></div>
<script src="index.js"></script>
  • index.jsの作成
index.js
import React from 'react';
import { render } from 'react-dom';
import App from './src/App';

render(<App />, document.getElementById('root'));
  • src/App.jsの作成
src/App.js
import React from 'react';

function App() {
  return (
    <div>
      <h1>Hello</h1>
    </div>
  );
}

export default App;
  • 起動
    • 今回は手っ取り早く試すためParcelを使います
npx parcel-bundler index.html

ApolloClientのセットアップ

  • ここからが本題です
  • まずはライブラリを追加します
yarn add @apollo/client graphql
  • 次にGraphQLサーバのアクセス先などを設定します
    • ApolloClientの公式ページに登場するTODOアプリのGraphQLサーバを使っています
    • ブラウザで https://plp0mopxq.sse.codesandbox.io/ にアクセスするとPlaygroundでスキーマなどを確認できます
src/graphql/client.js
import { ApolloClient, HttpLink, InMemoryCache } from '@apollo/client';

export default new ApolloClient({
  cache: new InMemoryCache(),
  link: new HttpLink({
    uri: 'https://plp0mopxq.sse.codesandbox.io/graphql',
  }),
});

  • App.jsに反映します
src/App.js
import React from 'react';
import { ApolloProvider } from '@apollo/client';
import client from './graphql/client';

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

export default App;
  • これでセットアップ完了です

Queryを実行する

  • 実行するQueryを定義するファイルを作成します
    • Todoの全量を取得するGET_TODOSと、指定したIDのTodoを1件取得するGET_TODOの2つを定義します
    • Query実行時に引数を渡す場合はGET_TODOで定義しているような書き方をします
src/graphql/query.js
import { gql } from '@apollo/client';

// 全件取得
export const GET_TODOS = gql`
  query getTodos {
    todos {
      id
      type
    }
  }
`;

// 指定したIDのTODOを1件取得
export const GET_TODO = gql`
  query getTodo($id: String!) {
    todo(id: $id) {
      id
      type
    }
  }
`;

useQuery

  • コンポーネントからGET_TODOSのQueryを呼び出します
  • useQueryを使うとコンポーネントが生成された時にQueryが実行されます
    • 今回のように一覧を取得して表示するようなケースで使うイメージですね
    • 今回実行するGET_TODOSは引数なしですが、ありの場合はuseQueryの第2引数で指定することができます
      • useQuery(GET_TODOS, { variables: { xx: 'xx' } })
src/components/TodoList.js
import React from 'react';
import { useQuery } from '@apollo/client';
import { GET_TODOS } from '../graphql/query';

function TodoList() {
  // Queryを実行
  const { loading, error, data } = useQuery(GET_TODOS);

  if (loading) return <p>...loading</p>;
  if (error) return <p>{error.message}</p>;

  return (
    <div>
      <h2>TodoList</h2>
      {data.todos.map(todo => (
        <p key={todo.id}>{todo.id}: {todo.type}</p>
      ))}
    </div>
  );
}

export default TodoList;
  • App.jsに反映します
src/App.js
import React from 'react';
import { ApolloProvider } from '@apollo/client';
import client from './graphql/client';
// importを追加
import TodoList from './components/TodoList';

function App() {
  return (
    <ApolloProvider client={client}>
      <h1>Hello</h1>
      {/* コンポーネントを追加 */}
      <TodoList />
    </ApolloProvider>
  );
}

export default App;

todolist.gif

useLazyQuery

  • IDを指定してTodoを1件取得するGET_TODOをコンポーネントから呼び出します
  • ユーザのアクションなどをきっかけにQueryを実行したい時はuseLazyQueryを使います
  • Query実行時の引数は{ variables: {...} }といった形式でvariablesは固定文言でその中に必要な値をセットします
src/components/Todo.js
import React, { useRef } from 'react';
import { useLazyQuery } from '@apollo/client';
import { GET_TODO } from '../graphql/query';

function Todo() {
  const inputRef = useRef(null);
  // getTodoを呼び出すとQueryが実行される
  const [getTodo, { loading, error, data }] = useLazyQuery(GET_TODO);

  return (
    <div>
      <h2>Todo</h2>
      <div>
        <p>
          <input ref={inputRef} />
          {/* clickされたらgetTodoを実行する */}
          <button onClick={() => getTodo({ variables: { id: inputRef.current.value } })}>
            GET
          </button>
        </p>
        {loading && <p>...loading</p>}
        {error && <p>{error.message}</p>}
        {data && <p>ID: {data.todo.id}, Todo: {data.todo.type}</p>}
      </div>
    </div>
  );
}

export default Todo;
  • App.jsに反映します
src/App.js
import React from 'react';
import { ApolloProvider } from '@apollo/client';
import client from './graphql/client';
import TodoList from './components/TodoList';
// importを追加
import Todo from './components/Todo';

function App() {
  return (
    <ApolloProvider client={client}>
      <h1>Hello</h1>
      <TodoList />
      {/* コンポーネントを追加 */}
      <Todo />
    </ApolloProvider>
  );
}

export default App;

todo.gif

Mutationを実行する

  • 実行するMutationを定義するファイルを作成します
    • Todoを追加するADD_TODOを定義します
    • 引数の書き方はQueryの時と同じです
src/query/mutation.js
import { gql } from '@apollo/client';

export const ADD_TODO = gql`
  mutation AddTodo($type: String!) {
    addPlayer(type: $type) {
      id
      type
    }
  }
`;

useMutation

  • コンポーネントからMutationを実行してみます
  • useMutationから取得できるaddTodoを呼び出すとMutationを実行できます
src/components/AddTodo.js
import React, { useRef } from 'react';
import { useMutation } from '@apollo/client';
import { ADD_TODO } from '../graphql/mutation';

function AddTodo() {
  const inputRef = useRef(null);
  const [addTodo, { loading, error }] = useMutation(ADD_TODO);

  return (
    <div>
      <h2>AddTodo</h2>
      {loading && <p>...loading</p>}
      {error && <p>{error.message}</p>}
      <p>
        <input ref={inputRef} />
        {/* クリックしたらaddTodoを実行する */}
        <button onClick={() => addTodo({ variables: { type: inputRef.current.value } })}>
          ADD
        </button>
      </p>
    </div>
  );
}

export default AddTodo;
  • App.jsに反映します
src/App.js
import React from 'react';
import { ApolloProvider } from '@apollo/client';
import client from './graphql/client';
import TodoList from './components/TodoList';
import Todo from './components/Todo';
// importを追加
import AddTodo from './components/AddTodo';

function App() {
  return (
    <ApolloProvider client={client}>
      <h1>Hello</h1>
      <TodoList />
      <Todo />
      {/* コンポーネントを追加 */}
      <AddTodo />
    </ApolloProvider>
  );
}

export default App;

add-todo.gif

  • 最後におまけで、追加したTodoを画面に反映させたいのでTodoListを再取得するボタンを追加します
src/components/TodoList.js
import React from 'react';
import { useQuery } from '@apollo/client';
import { GET_TODOS } from '../graphql/query';

function TodoList() {
  // refetchを追加
  const { loading, error, data, refetch } = useQuery(GET_TODOS);

  if (loading) return <p>...loading</p>;
  if (error) return <p>{error.message}</p>;

  return (
    <div>
      <h2>TodoList</h2>
      {/* クリックするとデータを再取得する */}
      <button onClick={() => refetch()}>REFETCH</button>
      {data.todos.map(todo => (
        <p key={todo.id}>{todo.id}: {todo.type}</p>
      ))}
    </div>
  );
}

export default TodoList;

refetch.gif

まとめ

  • Aplloを使うとloadingの管理などもやってくれるため本当に便利ですね
  • とりあえずuseQuery, useLazyQuery, useMutationの3つが使えればだいたいのことはできると思います
  • これらを使った実装がとても簡単だと実感してもらえていれば幸いです
22
13
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
22
13

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?