Next.js_4 (CRUD)

■9. ライブラリのファイルを作成

pages/にhasura-crud.tsxを作成

9-2. 必要なインポートと基本形

import { VFC, useState, FormEvent } from 'react'
import { useQuery, useMutation } from '@apollo/client'
import {
  GET_USERS,
  CREATE_USER,
  DELETE_USER,
  UPDATE_USER,
} from '../queries/queries'
import {
  GetUsersQuery,
  CreateUserMutation,
  DeleteUserMutation,
  UpdateUserMutation,
} from '../types/generated/graphql'
import { Layout } from '../components/Layout'

const HasuraCRUD: VFC = () => {

  return(
  )
}

mutationも自動生成の命名規則はqueryと同じ。

useQueryを定義。

import { VFC, useState, FormEvent } from 'react'
import { useQuery, useMutation } from '@apollo/client'
import {
  GET_USERS,
  CREATE_USER,
  DELETE_USER,
  UPDATE_USER,
} from '../queries/queries'
import {
  GetUsersQuery,
  CreateUserMutation,
  DeleteUserMutation,
  UpdateUserMutation,
} from '../types/generated/graphql'
import { Layout } from '../components/Layout'

const HasuraCRUD: VFC = () => {
  const { data, error } = useQuery<GetUsersQuery>(GET_USERS, {
    fetchPolicy: 'cache-and-network',
  })

  return(
  )
}

ユーザーの一覧をHasuraから取得

Layoutコンポーネントを設置

import { VFC, useState, FormEvent } from 'react'
import { useQuery, useMutation } from '@apollo/client'
import {
  GET_USERS,
  CREATE_USER,
  DELETE_USER,
  UPDATE_USER,
} from '../queries/queries'
import {
  GetUsersQuery,
  CreateUserMutation,
  DeleteUserMutation,
  UpdateUserMutation,
} from '../types/generated/graphql'
import { Layout } from '../components/Layout'

const HasuraCRUD: VFC = () => {
  const { data, error } = useQuery<GetUsersQuery>(GET_USERS, {
    fetchPolicy: 'cache-and-network',
  })

  return(
    <Layout title="Hasura CRUD">
      <p className="mb-3 font-bold">Hasura CRUD</p>
    </Layout>
  )
}

mutation関係の処理を書く。

■updateの処理

~省略~

const HasuraCRUD: VFC = () => {
  const { data, error } = useQuery<GetUsersQuery>(GET_USERS, {
    fetchPolicy: 'cache-and-network',
  })
  const [update_users_by_pk] = useMutation<UpdateUserMutation>(UPDATE_USER)
  return(
    <Layout title="Hasura CRUD">
      <p className="mb-3 font-bold">Hasura CRUD</p>
    </Layout>
  )
}

useMutationを使って、「UpdateUserMutation」を実行する。この時のデータ型はジェネリックスでインポートしておく()。

■createの処理

~省略~

const HasuraCRUD: VFC = () => {
  const { data, error } = useQuery<GetUsersQuery>(GET_USERS, {
    fetchPolicy: 'cache-and-network',
  })
  const [update_users_by_pk] = useMutation<UpdateUserMutation>(UPDATE_USER)
  const [insert_users_one] = useMutation<CreateUserMutation>(CREATE_USER, {
    update(cache, { data: { insert_users_one } }) {
      const cacheId = cache.identify(insert_users_one)
      cache.modify({
        fields: {
          users(existingUsers, { toReference }) {
            return [toReference(cacheId), ...existingUsers]
          },
        },
      })
    },
  })
  return(
    <Layout title="Hasura CRUD">
      <p className="mb-3 font-bold">Hasura CRUD</p>
    </Layout>
  )
}

基本は同じだが、createとdeleteの場合は、キャッシュが自動的にクリアされない仕様になっているため(updateはapolloが自動的に更新してくれるが)、createとdeleteは自分でキャッシュの後処理を書く必要がある。

具体的は、createの処理が終わった後に、updateという関数を使っていく。

そして、createUserで新しくユーザーを作った後に、作ったユーザーが帰ってくるが、返ってくるときのフィールドの名前というのが、insert_users_oneという名前のフィールドでuserが返ってくる。

ですので、updateの第2引数で、返ってきたdataの中身にある { insert_users_one }と、フィールドを読みにいくようにしていく。

そうすると、 insert_users_oneの変数のところに、ユーザーの情報が入ってくる。

そして、apolloの機能で、「cache.identify」で、ユーザーのキャッシュのIDを取得してくる。

cacheId というのは、insert_users_one のtype_nameとIDを組み合わせたキーが、cacheId に入ってくる。

そして、このcacheId を使って、これもapolloの機能だが、toReferenceというのが準備されていて、引数にcacheIdを渡してあげると、cacheIdに紐づいた{ insert_users_one }のデータを参照することができる。

そして、cache.modifyのfieldsのところは、更新したいフィールドを指定する。

今回はusersのキャッシュのフィールドをマニュアルで書き換えたいので、usersにして、そして第一引数では、既存のキャッシュの配列を取得することができるので、こちらをexistingUsersという名前で受け取るようにしておく。

そして、キャッシュの更新自体はシンプルで、既存のキャッシュをスプレッドで展開して、その先頭に、今つくったinsert_users_oneという新しいキャッシュのデータを、配列の先頭に足してあげる、というもの。

こうすることで、今新しく作ったユーザーの情報を、マニュアルでキャッシュの既存の配列に足すことができる。

■deleteの処理

~省略~

const HasuraCRUD: VFC = () => {
  const { data, error } = useQuery<GetUsersQuery>(GET_USERS, {
    fetchPolicy: 'cache-and-network',
  })
  const [update_users_by_pk] = useMutation<UpdateUserMutation>(UPDATE_USER)
  const [insert_users_one] = useMutation<CreateUserMutation>(CREATE_USER, {
    update(cache, { data: { insert_users_one } }) {
      const cacheId = cache.identify(insert_users_one)
      cache.modify({
        fields: {
          users(existingUsers, { toReference }) {
            return [toReference(cacheId), ...existingUsers]
          },
        },
      })
    },
  })
  const [delete_users_by_pk] = useMutation<DeleteUserMutation>(DELETE_USER, {
    update(cache, { data: { delete_users_by_pk } }) {
      cache.modify({
        fields: {
          users(existingUsers, { readField }) {
            return existingUsers.filter(
              (user) => delete_users_by_pk.id !== readField('id', user)
            )
          },
        },
      })
    },
  })
  return(
    <Layout title="Hasura CRUD">
      <p className="mb-3 font-bold">Hasura CRUD</p>
    </Layout>
  )
}

useMutationでDELETE_USERのコマンドを実行していく。

deleteの場合、イメージとしては、existingUsersの既存の配列から削除した配列を、マニュアルでfilterを使って削除する、という処理になる。

こちらもupdateで、今削除したフィールドが { delete_users_by_pk }で、今削除したユーザーがこのフィールドの名前で返ってくるので、このデータの中から取得して、今回も更新したいフィールドの名前はusersなので、users( にして、第一引数で既存のuserのキャッシュを取得。

そして、apolloで用意されている{ readField }という機能を使って、任意のフィールドの値を読みにいくことができて、今回はexistingUsersをfilterで展開して、userを1個1個展開していくが、その中から、userのidフィールドをreadFieldで読んで、そのidと今削除したdelete_users_by_pkのidを比較し、そして今削除したユーザーのidと一致しないuserだけをフィルターで残す、という処理をしている。

こうすることで、今削除したdelete_users_by_pkのidに一致するものだけが省かれて、キャッシュが更新できる形になる。

■editの処理

const [editedUser, setEditedUser] = useState({ id: '', name: '' })

を追加。

~省略~

const HasuraCRUD: VFC = () => {
  const [editedUser, setEditedUser] = useState({ id: '', name: '' })
  const { data, error } = useQuery<GetUsersQuery>(GET_USERS, {
    fetchPolicy: 'cache-and-network',
  })
  const [update_users_by_pk] = useMutation<UpdateUserMutation>(UPDATE_USER)
  const [insert_users_one] = useMutation<CreateUserMutation>(CREATE_USER, {
    update(cache, { data: { insert_users_one } }) {
      const cacheId = cache.identify(insert_users_one)
      cache.modify({
        fields: {
          users(existingUsers, { toReference }) {
            return [toReference(cacheId), ...existingUsers]
          },
        },
      })
    },
  })

~省略~

ユーザーが操作できるように、フォームを作成。

 ~省略~

 return(
    <Layout title="Hasura CRUD">
      <p className="mb-3 font-bold">Hasura CRUD</p>
      <form
        className="flex flex-col justify-center items-center"
        onSubmit={handleSubmit}
      >

      </form>
    </Layout>
  )
}