ApolloとReact Hooks

# はじめに

React Hooksの登場によりViewに関する処理と状態、ロジック、ライフサイクルを綺麗に分離することができるようになりました。そのような中、先日GraphQLクライアントであるApolloもReact Hooksの対応がリリースされました。

今までApolloを利用する際にはHoCやrender propを使う必要があり、可読性が低いという問題がありました。
そこで今回はReact Hooksを利用することでどのようにコードが簡潔に記述できるようになるかを紹介します。

## React Hooks以前のコード

公開されているGraphQL APIであるStarWars APIを利用します。

- GraphQLローディグ中

<img width="413" alt="loading" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/54186/033aa090-6a48-43cd-92f8-6bbe02917449.png">

- GraphQLロード完了

<img width="454" alt="done" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/54186/c4604236-a8cb-c526-087a-5bdb703f5640.png">



以下のコードでは、Queryコンポーネントの引数にクエリを渡して、内部の要素にロジックが書かれています。
ロード周りの処理がQueryコンポーネントの中身が想像できず理解が難しいです。


```javascript
const FETCH_MOVIES = gql`
  {
    allFilms {
      id
      title
    }
  }
`;

function StarWarsMovies() {
  return (
    <div className="App">
      <h2>Star Wars API Without React Hooks</h2>
      <div>
        <Query query={FETCH_MOVIES}>
          {({ data, error, loading }) => {
            if (loading) return "Loading...";
            if (error) return error;

            const films = data.allFilms.map(film => (
              <div key={film.id}>
                {film.title}
                <div />
              </div>
            ));
            return <div>{films}</div>;
          }}
        </Query>
      </div>
    </div>
  );
}
```

https://codesandbox.io/s/star-wars-7ukuh


## React HooksであるuseQueryを利用したコード

実行結果は先ほどと同じです。

以下のコードではuseQueryの引数にクエリーを渡すろとloading、error、レスポンスの3つが返ってきます。
とても直感的な関数です。あとはこの返ってきた値を素直に利用するコードを書くだけです。
render propと比べて階層が減ることで、とてもシンプルに記述できるようになりました。

```javascript
function StarWarsMovies() {
  const { loading, error, data } = useQuery(FETCH_MOVIES);
  if (loading) {
    return "Loading...";
  }

  if (error) {
    return error;
  }

  const films = data.allFilms.map(film => (
    <div key={film.id}>{film.title}</div>
  ));

  return (
    <div className="App">
      <h2>Star Wars API Without React Hooks</h2>
      {films}
    </div>
  );
}
```

https://codesandbox.io/s/star-wars-0qq34

## React HooksであるuseLazyQueryを利用したコード

useLazyQueryは、この関数を実行した時点ではクエリーのリクエストは投げられず関数が返されます。
この返された関数を実行することで、クエリーのリクエストが投げられます。
ボタンクリック時などアクション後にクエリーを実行したい場合に利用します。

こちらは実行されていなければボタンを表示して、ボタンが押されるとGraphQLのリクエストが投げられます。
それ以外は先ほどと同じです。

<img width="422" alt="スクリーンショット 2019-12-24 23.11.42.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/54186/665dc1b1-ef87-d373-98ab-ce9bf7dd473f.png">



```javascript
function StarWarsMovies() {
  const [loadStarWarsMovies, { called, loading, error, data }] = useLazyQuery(FETCH_MOVIES);
  if (called && loading) {
    return "Loading...";
  }

  if (!called) {
    return <button onClick={loadStarWarsMovies}>Load Star Wars Movies</button>;
  }

  if (error) {
    return error;
  }

  const films = data.allFilms.map(film => (
    <div key={film.id}>{film.title}</div>
  ));

  return (
    <div className="App">
      <h2>Star Wars API Without React Hooks</h2>
      {films}
    </div>
  );
}
```

https://codesandbox.io/s/star-wars-xwox7


## まとめ

React HooksのuseQueryによりHoCやrender propに比べてとても簡潔にコードが書けるようになりました。
公式ドキュメントでもReact HooksのuseQueryが標準の説明として利用されているため積極的に使うことが推奨されています。

また今回説明している以外にもReact Hooksには他にも様々なメリットがあるので是非ご覧ください。

- [builders Tokyo 2019 - 宣言的UI](https://speakerdeck.com/sonatard/xuan-yan-de-ui?slide=61)
- [React Hooksのカスタムフックが実現する世界 - オブジェクト指向とOSS](https://qiita.com/sonatard/items/617f324228f75b9c802f)