WASD TECH BLOG

WASD Inc.のTech Blogです。

Hasuraを使ってReactでTodoアプリをサクッと作ろう 完結編

WASD Inc. で開発をしている Yutorin です。

弊社のデジちゃいむでも採用しているHasuraとReactを使ってサクッとTODOアプリを引き続き作っていきましょう。

前回はこちらです。

tech.wasd-inc.com

今回は実際にHasuraとReactアプリとのつなぎ込みを行い、超簡単なTodoアプリを作っていきます。




目次


はじめに

前回までに作成していたリポジトリを使用します。
こちら からもダウンロードできます。
DockerとReact立ち上げをした状態で行います。

Apolloを導入する

ReactアプリがHasuraと通信するために、Apollo Clientを導入します。

yarn add @apollo/client graphql をしてパッケージをダウンロードしましょう。

src/components/App.tsx などの一番上の階層のコンポーネントで、Apollo Clientのインスタンスを作成します。

import { ApolloClient, ApolloProvider, HttpLink, InMemoryCache } from "@apollo/client";

const createApolloClient = (authToken?: string) =>
  new ApolloClient({
    link: new HttpLink({
      uri: "http://localhost:8080/v1/graphql",
      // * Auth0やAWS Cognitoなどを入れている場合は、ここでトークンを受け取る //
      // headers: {
      //   Authorization: `Bearer ${authToken}`,
      // },
    }),
    cache: new InMemoryCache(),
  });

このclientインスタンスを、 <ApolloProvider /> に渡します。

const App = () => {
  const client = createApolloClient();

  return (
    <ApolloProvider client={client}>
      <div>
          ...
      </div>
    </ApolloProvider>
  );

これでHasuraと通信する準備は完了です。

Queryを動かしてTodo一覧をとる

早速DB上のTodo一覧を持ってきましょう。
前回codegenしたTodoクエリを使用して、実際にページに反映していきましょう。
components/Todo/TodoPrivateList.tsx 内のハードコーディングされたtodosを置き換えます。

そのまえに、Todoを表示するコンポーネントの型から整理していきます。
生成されたクエリから型の返り値を引っ張れるので、Todo単体の型を取り出して、引数の型に設定します。
is_completedまわりは今は実装しないので、エラーが出ている部分をコメントアウトしていきます。

// TodoItem
import { TodoQuery } from "../../graphql/generated";

export type TodoType = TodoQuery["todos"][number];

type TodoItemType = {
  index: number;
  todo: TodoType;
}

(型の取り出し方がイケてないですが、Fragmentを使うといい感じにできます、今回は省略)

TodoFilters コンポーネントや、それに渡している関数などをコメントアウトしましょう。

useQuery() を使用してhooksの形式で受け取れます。

// TodoPrivateList.tsx
import { useQuery } from "@apollo/client";
import TodoItem, { TodoType } from "./TodoItem";

/* 中略 */

const { data: { todos } = {} } = useQuery(TodoDocument);

const todoList = todos.map((todo: TodoType, index: number) => 
    <TodoItem key={todo.id} index={index} todo={todo} />);

中にTodosDocumentを入れて、そこから出てくるdataをmapさせると、 DBに入っているデータが出てきます。

すごい

Mutationを動かして追加する

次はDBにデータを追加しましょう。

Hasura Consoleを開いて、左下にある Add new Query の Query を Mutation にして + を押します。
すると、ExplorerとGraphiQLがMutationを入力するモードに変わるので、前回Queryを作成した時と同じように作成していきます。

insert_todo_one をクリックして、object を開いて引数を入れていきますが、objectにある $ マークをクリックすると、引数が自動的に挿入されます。

その上で、mutationを動かしたあとの返り値としてid, name, created_atを追加するとこのようになります。

mutation CreateTodo($object: todo_insert_input!) {
  insert_todo_one(object: $object) {
    id
    name
    created_at
  }
}

引数の型の!は引数が必須という意味です。

src/graphql/mutations/CreateTodo.graphql を作成して、 yarn codegen してください。

ApollouseMutation() を使用して出てくる関数を使用します。
配列の最初に来る関数をSubmitの処理に入れます。

// Todo/TodoInput.tsx
  const [createTodo] = useMutation(CreateTodoDocument);

  const handleSubmit = (event: React.FormEvent<HTMLFormElement>) => {
    event.preventDefault();
    try {
      createTodo({
        variables: {
          object: {
            name: todoInput,
          },
        },
      });
      setTodoInput("");
    } catch (error) {
      // エラー処理
      console.error(error);
    }
  };

この状態でinputに入力することで新たにTodoが追加されますが、特に変わったように見えません。
ページを更新すると入力した値が出てきますが、入力した際に更新されるようにしたいので、useMutationに引数を追加します。

  const [createTodo] = useMutation(CreateTodoDocument, {
    refetchQueries: [TodoDocument], // useQueryに使ったものと同じ
  });

refetchQueries にクエリと同じ変数、または "Todo" とクエリの名前と同じ値を入れると、Mutationが動く際に指定したクエリを再取得することができます。

余計な通信が発生する分キャッシュを操作する方がより良いですが、今回は割愛します  
これでTodo一覧にTodoを追加できるようになりました。

これから

今回のハンズオンではここまでですが、まだまだ追加できるところがあると思います。
Mutationを update にして完了機能を実装してみたり、 delete を使って削除できるようにしてみたり、 pollinterval を使って定期的に更新させてみたり、Auth0でのログイン・ログアウト機能、他のユーザーとの連携、DBの操作権限設定などなど...
HasuraとApollo Clientには様々な機能がありますし常に追加されているので、ドキュメントなどを読み漁ってみるのをおすすめします。
 
また、今回のコードはこちらにあります。

あとがき

三部に渡るこの超大作(?)も1年半かけてようやくゴールです。
Hasuraのバージョンも現在では2.20.0とメジャーバージョンが変わってしまいましたが、現在でも生かせる部分があると思います。

このようにDBからフロントエンドまで一気通貫に簡単に作れるのがこの技術スタックのいいところなので、よかったら試しに色々触ってみてください。


 
WASD TECH BLOG では主に React、TypeScript、GraphQL に関する記事を定期的に発信していきます。
よろしければ他の記事や、 もお願いします。