0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

React(Next.js/GraphQL)からHasuraを使ったCRUD操作のためのユーザーの一覧を表示させる部分の作成

Last updated at Posted at 2021-11-19

はじめに

CRUD操作のための処理部分をこれまで作成してきました。

詳細👇

前回は、Stateの定義やformコンポーネントの作成をしました。

今回からは、ブラウザを通じでCRUD操作を行うためのインターフェイス部分を何回かに分けて作成していきます。

具体的には下記を作成していきます。

  • Stateの定義
  • クリックイベントの処理
  • コンポーネント(レイアウト)の用意
    • form部分
    • ユーザーの一覧を表示させる部分 👈 本記事はコレについて書いてます

最終的なゴール

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

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

目的

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

ユーザーの一覧を表示させる部分の作成

pages/hasura-crud.tsx
const { data, error } = useQuery<GetUsersQuery>(GET_USERS, {fetchPolicy: 'cache-and-network'})

上記の処理で取得したユーザーの一覧情報を表示する部分のコンポーネントを作成します。

作成する場所は

で、作成したformコンポーネントの下の部分にします。

ユーザー情報を表示する部分は別コンポーネントを作成します。

components/UserItem.tsx

コンポーネントの全体像は、下記のような感じです。

Hasura_CRUD.png

1ユーザー単位で、作成ボタンや削除ボタンを作るイメージですね。

まずは、必要なモジュールをimportします。

components/UserItem.tsx
import { VFC, memo, Dispatch, SetStateAction } from 'react'
import { Users, DeleteUserMutationFn } from '../types/generated/graphql'

表示最適化用にmemoをインポート。

DispatchSetStateActionは、useStateで作成された更新用のデータ型を定義する際に用います。

そしてGraphQLから自動生成されたデータ型から、Users と DeleteUserMutationFn をimportしています。

DeleteUserMutationFnこちらは、削除用の関数のデータ型です。

コンポーネントのpropsの型情報

このコンポーネントに渡すpropsは、以下かなーと察しがつきますね。

  • 表示するユーザーデータ
  • 更新の関数(setEditedUser
  • 削除用の関数(delete_users_by_pk

Propsのデータ型をジェネリクスで指定したいので、そのためのインターフェイスを作成します。

components/UserItem.tsx
interface Props {
  user: {
    __typename?: 'users'
  } & Pick<Users, 'id' | 'name' | 'created_at'>
  delete_users_by_pk: DeleteUserMutationFn
  setEditedUser: Dispatch<
    SetStateAction<{
      id: string
      name: string
    }>
  >
}

userのデータ型は、__typenameの属性とUsersから必要なデータ(id, name, created_at)をPickしています。

Usersの全データ型はホバーすると見れたりします。

スクリーンショット 2021-11-19 16.57.07.png

削除用の関数 delete_users_by_pk に必要な型は、import したモジュールをそのまま使用しています。

更新用の関数 setEditedUser に必要な型は、ホバーして表示させて、その内容をそのままコピペします。

コンポーネントの作成

最終的に作るコンポーネントの構成は以下です。

スクリーンショット 2021-11-19 17.49.46.png

表示部分や、更新用のボタンは難しくないと思いますので説明はSKIPします。

削除用ボタンは少し説明が必要かもしれません。

削除用ボタンのonClickの関数は通信処理が必要なので非同期で実行させています。

なので、onClickの中に無名アロー関数を入れて、クリック時にpropsで受け取った関数delete_users_by_pkが走るようにします。

関数delete_users_by_pkの実行には、削除対象のユーザーidが必要なので、それもuserとしてpropsで受け取っているのでuser.id指定しています。

delete_users_by_pk({ variables: { id: user.id} })

最終的に完成したのが以下です。

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'>
  delete_users_by_pk: DeleteUserMutationFn
  setEditedUser: Dispatch<
    SetStateAction<{
      id: string
      name: string
    }>
  >
}

export const UserItem: VFC<Props> = ({
  user,
  delete_users_by_pk,
  setEditedUser,
}) => {
  return (
    <div className="my-1">
      <span className="mr-2">{user.name}</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>
  )
}

再レンダリングを防止するための処理

上記のままだと、親コンポーネントにあるinput要素が更新するたびに、関数setEditedUserが走り、その都度

子コンポーネントである<UserItem />がレンダリングされます。

入力中の内容は、子コンポーネント<UserItem />に関係ないので、

親コンポーネントにあるinput要素が更新されても、子コンポーネント<UserItem />が再レンダリングされないようにします。

具体的には、子コンポーネント<UserItem />memoを追加します。

export const UserItem: VFC<Props> = memo(
  // 省略
)

最終的な完成形

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'>
  delete_users_by_pk: DeleteUserMutationFn
  setEditedUser: Dispatch<
    SetStateAction<{
      id: string
      name: string
    }>
  >
}

export const UserItem: VFC<Props> = memo(
  ({ user, delete_users_by_pk, setEditedUser }) => {
    return (
      <div className="my-1">
        <span className="mr-2">{user.name}</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>
    )
  }
)

UserItemをimportする

先ほど作成したコンポーネントをimportしていきましょう。

formコンポーネントの下に表示させるので、以下の場所に書きます。

pages/hasura-crud.tsx
</form>

{data?.users.map((user) => {
  return (
    <UserItem
      key={user.id}
      user={user}
      setEditedUser={setEditedUser}
      delete_users_by_pk={delete_users_by_pk}
    />
  )
})}

dataがない場合もあるので?がつきます。data?.usersは配列なので、関数mapで要素1つ1つを処理できます。

最終的にreturnするコンポーネントは先ほど作成した<UserItem />で、渡すporpsは

user={user}
setEditedUser={setEditedUser}
delete_users_by_pk={delete_users_by_pk}

で、問題ないですね。(key={user.id})は、ユニークに識別できるものの指定がいるので書いてるだけです。

今日のところは以上です。

次回は、完成した機能を実際に想定通りの挙動かを検証してみます。

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

0
0
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
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?