Next.js_4 (CRUD)
■9. ライブラリのファイルを作成
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> ) }