はじめに
この記事は2020年のRevCommアドベントカレンダー12日目の記事です。11日目は @hiratake55 さんの RevCommにおけるリモート開発を支える取り組みを紹介します でした。
こんにちは、フロントエンドエンジニアの山田です。
普段の業務ではReactを使ってアプリケーション開発をしています。
本日のお題は「Apollo Clientでwebsocket通信してみよう」です。Day 5でApollo Server構築の記事がありますので、こちら使ってフロントの作成を試みます。
今回のアプリケーションでできること
- データの取得
- データの作成
- データのサブスクライブ
目次
- 準備
- Apollo Clientの作成
- Queryを定義
- 描画
- まとめ
準備
さて、今回はReactを使っていきます。
npx create-react-app apollo-client
必要なパッケージをインストール
yarn add @apollo/client subscriptions-transport-ws
Apollo Clientの作成
GraphQLとのやりとりをすべてWebSocketで行うので、エンドポイントをws://localhost:4000/graphql
とします。
SubscriptionsClient
インスタンスをネットワークインターフェイスとして使用しApolloClient
インスタンスを作成します。
import { ApolloClient, InMemoryCache } from '@apollo/client';
import { SubscriptionClient } from 'subscriptions-transport-ws';
import { WebSocketLink } from '@apollo/client/link/ws';
const cache = new InMemoryCache();
const wsClient = new SubscriptionClient('ws://localhost:4000/graphql', {
reconnect: true,
});
const wsLink = new WebSocketLink(wsClient);
export const client = new ApolloClient({
cache,
link: wsLink,
});
ApolloProviderをWrapしていきます。
import React from 'react';
import ReactDOM from 'react-dom';
import { ApolloProvider } from '@apollo/client';
import App from './App';
import { client } from './apollo/client';
import reportWebVitals from './reportWebVitals';
ReactDOM.render(
<React.StrictMode>
<ApolloProvider client={client}>
<App />
</ApolloProvider>
</React.StrictMode>,
document.getElementById('root')
);// If you want to start measuring performance in your app, pass a function
// to log results (for example: reportWebVitals(console.log))
// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
reportWebVitals();
Queryを定義
バックエンドをもとにgraphqlのqueryを作成します。
import { gql } from '@apollo/client';
export const GET_BOOKS = gql`
query books {
books {
title
author
}
}
`;
export const ADD_BOOKS = gql`
mutation AddBook($title: String!, $author: String!) {
addBook(title: $title, author: $author) {
title
author
}
}
`;
export const SUBSCRIBE_BOOKS = gql`
subscription {
bookAdded {
title
author
}
}
`;
描画
最後の仕上げです。
ここで大事なのはsubscribeToMore
を使ってデータの監視を行うことです。
-
document:は
subscription
のクエリを設定します。 - updateQueryはクエリの現在キャッシュされている結果(prev)をGraphQLサーバーによってプッシュされたsubscriptionDataと組み合わせる方法をApolloクライアントに指示する関数です。この関数の戻り値は、クエリの現在のキャッシュ結果を完全に置き換えます。
今回の例ではuseMutation
で作成されたデータをsubscribeToMore
で監視し、prev
のデータに追加することでリストの更新が行われる仕組みとなっています。
import React, { useEffect, useState } from 'react';
import { useMutation, useLazyQuery } from '@apollo/client';
import { GET_BOOKS, ADD_BOOKS, SUBSCRIBE_BOOKS } from './apollo/query/book';
const App = () => {
const [author, setAuthor] = useState('');
const [title, setTitle] = useState('');
const [getBooks, { data, called, subscribeToMore, refetch }] = useLazyQuery(GET_BOOKS);
const [addBook] = useMutation(ADD_BOOKS);
useEffect(() => {
if (!called) {
getBooks();
}
}, [called, getBooks]);
useEffect(() => {
if (refetch && called) {
refetch();
}
}, [called, refetch]);
useEffect(() => {
if (subscribeToMore) {
subscribeToMore({
document: SUBSCRIBE_BOOKS,
updateQuery: (prev, { subscriptionData }) => {
if (!subscriptionData.data) return prev;
const newBook = subscriptionData.data.bookAdded;
const updatedBooks = [...prev.books, newBook];
return { books: updatedBooks }
}
});
}
}, [subscribeToMore]);
const handleAuthorChange = (e) => {
setAuthor(e.target.value);
};
const handleTitleChange = (e) => {
setTitle(e.target.value);
};
const handleSubmit = () => {
addBook({ variables: { title, author }});
setAuthor('');
setTitle('');
};
return (
<div style={{ padding: 20 }}>
<div style={{ display: "flex", alignItems: "flex-end" }}>
<div>
<p style={{ margin: 0 }}>タイトル</p>
<input type="text" name="title" value={title} onChange={handleTitleChange} />
</div>
<div style={{ marginLeft: 20 }}>
<p style={{ margin: 0 }}>著者名</p>
<input type="text" name="author" value={author} onChange={handleAuthorChange} />
</div>
<div style={{ marginLeft: 10 }}>
<button style={{ cursor: "pointer" }} type="submit" onClick={handleSubmit}>登録</button>
</div>
</div>
{data && (
<ul>
{data.books.map((book, i) => {
return (
<li key={i}>{`${book.title} (${book.author})`}</li>
)
})}
</ul>
)}
</div>
);
}
export default App;
まとめ
いかがでしたでしょうか?
今回はApollo Clientでwebsocket通信をしてみました。
Revcommで一緒に働いてくれる方を絶賛募集しています。
募集中のポジションは、株式会社RevComm 採用情報 をご覧ください。
次回は@shintaromatsudoのさんの記事になります。