はじめに
React(Next.js/GraphQL)からHasuraを使ったCRUD操作を何回かに分けて見ていきます。
今回は、ざっとCRUD全体の処理を作成して、実機で動くところまで見ていきます。
個々の処理やその説明は何回かに分けて説明します。
最終的なゴール
以下のような構成のアプリを作ることです。
目的
- 仕事で使っている技術のキャッチアップと復習
- 使う可能性がある技術の理解度向上
前回のおさらい
apolloの4つの fetchPolicy を紹介しました
詳細👇
ファイルの用意
以下のファイルを作成します。
pages/hasura-crud.tsx
components/UserItem.tsx
必要なモジュールのimport
pages/hasura-crud.tsx
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'
CRUD
のCUD
はuseMutation
が必要なのでimportしています。
クエリ
import {
GetUsersQuery,
CreateUserMutation,
DeleteUserMutation,
UpdateUserMutation,
} from '../types/generated/graphql'
も、使用するのでimportします。
実態は自動作成されたtypes/generated/graphql.tsx
にあります。
pages/hasura-crud.tsx
pages/hasura-main.tsx
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'
import { UserItem } from '../components/UserItem'
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]
},
},
})
},
})
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)
)
},
},
})
},
})
const handleSubmit = async (e: FormEvent<HTMLFormElement>) => {
e.preventDefault()
if (editedUser.id) {
try {
await update_users_by_pk({
variables: {
id: editedUser.id,
name: editedUser.name,
},
})
} catch (err) {
alert(err.message)
}
setEditedUser({ id: '', name: '' })
} else {
try {
await insert_users_one({
variables: {
name: editedUser.name,
},
})
} catch (err) {
alert(err.message)
}
setEditedUser({ id: '', name: '' })
}
}
if (error) return <Layout title="Hasura CRUD">Error: {error.message}</Layout>
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}
>
<input
className="px-3 py-2 border border-gray-300"
placeholder="New user ?"
type="text"
value={editedUser.name}
onChange={(e) =>
setEditedUser({ ...editedUser, name: e.target.value })
}
/>
<button
disabled={!editedUser.name}
className="disabled:opacity-40 my-3 py-1 px-3 text-white bg-indigo-600 hover:bg-indigo-700 rounded-2xl focus:outline-none"
data-testid="new"
type="submit"
>
{editedUser.id ? 'Update' : 'Create'}
</button>
</form>
{data?.users.map((user) => {
return (
<UserItem
key={user.id}
user={user}
setEditedUser={setEditedUser}
delete_users_by_pk={delete_users_by_pk}
/>
)
})}
</Layout>
)
}
export default HasuraCRUD
components/UserItem.tsx
components/UserItem.tsx
import { VFC, memo, Dispatch, SetStateAction } from 'react'
import { Users, DeleteUserMutationFn } from '../types/generated/graphql'
interface Props {
user: {
__typename?: 'users'
} & Pick<Users, 'id' | 'name' | 'created_at'>
delete_users_by_pk: DeleteUserMutationFn
setEditedUser: Dispatch<
SetStateAction<{
id: string
name: string
}>
>
}
export const UserItem: VFC<Props> = memo(
({ user, delete_users_by_pk, setEditedUser }) => {
//console.log('UserItem rendered')
return (
<div className="my-1">
<span className="mr-2">{user.name}</span>
<span className="mr-2">{user.created_at}</span>
<button
className="mr-1 py-1 px-3 text-white bg-green-600 hover:bg-green-700 rounded-2xl focus:outline-none"
data-testid={`edit-${user.id}`}
onClick={() => {
setEditedUser(user)
}}
>
Edit
</button>
<button
className="py-1 px-3 text-white bg-pink-600 hover:bg-pink-700 rounded-2xl focus:outline-none"
data-testid={`delete-${user.id}`}
onClick={async () => {
await delete_users_by_pk({
variables: {
id: user.id,
},
})
}}
>
Delete
</button>
</div>
)
}
)
ブラウザ確認して、以下にアクセスしてください。
http://localhost:3000/hasura-crud
以下の処理ができれば成功です
- 以前、Hasura上で作成したユーザーが表示される
- Insert用のInput要素が表示されて、新規ユーザーが作成できる口ができる
- 各ユーザーごとedit, detele処理ができるボタンができて、それが実行できる
今日のところは以上です。
次回以降で、CRUD各処理の詳細を見てきます。
アウトプット100本ノック実施中