概要
- 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
- http://localhost:1234 で起動します
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
で定義しているような書き方をします
- 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;
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;
Mutationを実行する
- 実行するMutationを定義するファイルを作成します
- Todoを追加する
ADD_TODO
を定義します - 引数の書き方はQueryの時と同じです
- Todoを追加する
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;
- 最後におまけで、追加した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;
まとめ
- Aplloを使うとloadingの管理などもやってくれるため本当に便利ですね
- とりあえず
useQuery
,useLazyQuery
,useMutation
の3つが使えればだいたいのことはできると思います - これらを使った実装がとても簡単だと実感してもらえていれば幸いです