Hasura, Apollo, Reactでsubscriptionsのリアルタイム更新の実装

メモ書き
需要が有れば書き直します。

# 実装コード

httpの通信からWebSocketの通信に切り替える必要があるのでプラグインのインストール

```bash
yarn add subscriptions-transport-ws
```

### WebSocketLinkを使ってApolloClientのlinkをwsに変える

uriでhttp指定していたURIが`http://localhost:8080/v1/graphql`であった
WebSocketで双方向に通信したいので`ws://localhost:8080/v1/graphql`に書き換える。

この時、Hasura側では特に何もする必要はない。

```tsx:index.tsx
import {
  ApolloClient,
  InMemoryCache,
  gql
} from "@apollo/client";
import { WebSocketLink } from "@apollo/client/link/ws";
import { useSubscription } from '@apollo/react-hooks'
import ArticleCards from '../components/ArticleCards'

const client = new ApolloClient({
  // uriをlinkに変更（http通信からws通信にする）
  link: new WebSocketLink({
    uri: 'ws://localhost:8080/v1/graphql',
    options: {
      reconnect: true,
       connectionParams: {
          headers: {
            // 認証系はここに書く
          }
       }
    }
  }),
  cache: new InMemoryCache(),
 });

export default function Home() {
  return (
    <ApolloProvider client={client}>
      <ArticleCards></ArticleCards>
    </ApolloProvider>
  );
}
```

### 記事一覧のコンポーネント（雑につくった）でリアルタイム更新

`useQuery`を使っていた場合は、`useSubscription`に書き換える。
gqlのコード以外は特に変わらないので説明は割愛

```tsx:ArticleCards.tsx
import { useSubscription } from '@apollo/react-hooks'
import Card from "@material-ui/core/Card";
import CardActions from "@material-ui/core/CardActions";
import CardContent from "@material-ui/core/CardContent";
import Button from "@material-ui/core/Button";
import Typography from "@material-ui/core/Typography";
import articleStyle from '../styles/ArticleCards.module.css';

import { ARTICLES_SUBSCRIPTIONS } from '../gql/articles';

interface ArticleType {
  id: string;
  text: string;
}

const Article = (article: ArticleType) => {
  return (
    <Card variant="outlined" className={articleStyle.card}>
      <CardContent>
        <Typography variant="body2" component="p">
          {article.text}
        </Typography>
      </CardContent>
      <CardActions>
        <Button size="small">MORE</Button>
      </CardActions>
    </Card>
  );
}

const ArticleCards = () => {
  const { loading, error, data} = useSubscription(ARTICLES_SUBSCRIPTIONS);
  console.log(data)
  // ローディング中の表示
  if (loading) return <p>loading</p>
  // エラー時の表示
  if (error) return <p>{error.toString()}</p>
  //　成功してデータが帰ってきた時の表示
  return (
    <div className={articleStyle.article}>
      {data.articles.map((article: ArticleType) => (
        <Article {...article} key={article.id}></Article>
      ))}
    </div>
  );
}

export default ArticleCards
```

### gqlでsubscriptionsを書く

参考までに`query`, `subscription`, `mutation`を書いておいた。

```ts:gql/articles.ts
import gql from "graphql-tag";

export const ARTICLES_QUERY = gql`
  query {
    articles {
      id
      text
    }
  }
`;

export const ARTICLES_SUBSCRIPTIONS = gql`
  subscription ArticlesSubscriptions {
    articles(limit: 30, order_by: { created_at: asc }) {
      date
      id
      text
    }
  }
`;

export const ADD_ARTICLES = gql`
  mutation AddArticles($text: String = "", $date: date = "") {
    insert_articles(objects: { text: $text, date: $date }) {
      returning {
        id
        text
        date
      }
    }
  }
`;

```

## 参考記事

Hasura/GraphQL/React.js/Subscriptions
https://hasura.io/learn/graphql/react/subscriptions/1-subscription/

Hasura/GraphQL/Vue.js/Subscriptions
https://hasura.io/learn/graphql/vue/subscriptions/1-subscription/

WebSocketとは？
https://www.keicode.com/script/html5-websocket-1.php