はじめに
React customHooks を利用してロジックとUIを切り離すまでをやってみたシリーズ
今回は、このシリーズの方針について書きます。
これまで
Hasuraにユーザーのデータと仮定してデータを作成し
データを外部APIとして取得できるようにして、Next.js(apollo)を使って、クエリやミューテーションできるようにしました
Hasuraから取得したユーザーデータの一覧ページと詳細ページを作りながら、SG
とISR
を学んできました
このシリーズの目次
- このシリーズの方針👈 今回はコレ
- customHooks を利用してロジックとUIを切り離すまで
最終的なゴール
以下のような構成のアプリを作ることです。
目的(最終的なゴールを達成する)
- 仕事で使っている技術のキャッチアップと復習
- 使う可能性がある技術の理解度向上
このシリーズの方針
Next.js(apollo)からHasuraに対してCRUD操作(クエリやミューテーション)をできるようにしました。
表側は特に問題ないかもですが
コードを見てもらうと課題点もあります。
クリックすると実際のコードが見れます
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'
import { BreadcrumbJsonLd } from 'next-seo'
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">
<BreadcrumbJsonLd
itemListElements={[
{
position: 1,
name: 'Books',
item: 'https://example.com/books',
},
{
position: 2,
name: 'Authors',
item: 'https://example.com/books/authors',
},
{
position: 3,
name: 'Ann Leckie',
item: 'https://example.com/books/authors/annleckie',
},
{
position: 4,
name: 'Ancillary Justice',
item: 'https://example.com/books/authors/ancillaryjustice',
},
]}
/>
<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
上記のコードを見ると、以下が混在している状況です。
- ロジック
-
useQuery
やuseMutation
などのapolloを使った通信処理 - submitした際の処理(関数
handleSubmit
)
-
- UI(コンポーネント)
これを、React の customHooksを利用してロジックとUIを切り離すことをこのシリーズではやっていきます。
また、コンポーネントも分離する関係で不要なレンダリングや処理が走るのを防止するためにmemo
やuseCallback
を用いたパフォーマンス改善も実装していきます。
まとめ
今回は、このシリーズの方針について書きました。
次回
アウトプット100本ノック実施中