LoginSignup
1
1

More than 1 year has passed since last update.

React customHooks を利用してロジックとUIを切り離すまでをやってみた(1/5)〜このシリーズの方針〜

Last updated at Posted at 2021-11-27

はじめに

React customHooks を利用してロジックとUIを切り離すまでをやってみたシリーズ

今回は、このシリーズの方針について書きます。

これまで

Hasuraにユーザーのデータと仮定してデータを作成し

データを外部APIとして取得できるようにして、Next.js(apollo)を使って、クエリやミューテーションできるようにしました

Hasuraから取得したユーザーデータの一覧ページと詳細ページを作りながら、SGISRを学んできました

このシリーズの目次

最終的なゴール

以下のような構成のアプリを作ることです。

スクリーンショット 2021-10-18 17.27.41.png

目的(最終的なゴールを達成する)

  • 仕事で使っている技術のキャッチアップと復習
  • 使う可能性がある技術の理解度向上

このシリーズの方針

Next.js(apollo)からHasuraに対してCRUD操作(クエリやミューテーション)をできるようにしました。

表側は特に問題ないかもですが

スクリーンショット 2021-11-28 0.15.26.png

コードを見てもらうと課題点もあります。

クリックすると実際のコードが見れます
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

上記のコードを見ると、以下が混在している状況です。

  • ロジック
    • useQueryuseMutationなどのapolloを使った通信処理
    • submitした際の処理(関数handleSubmit
  • UI(コンポーネント)

これを、React の customHooksを利用してロジックとUIを切り離すことをこのシリーズではやっていきます。

また、コンポーネントも分離する関係で不要なレンダリングや処理が走るのを防止するためにmemouseCallbackを用いたパフォーマンス改善も実装していきます。

まとめ

今回は、このシリーズの方針について書きました。

次回

アウトプット100本ノック実施中

1
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1