React customHooks を利用してロジックとUIを切り離すまでをやってみた（2/5）〜ロジックの作成〜

## はじめに

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

今回は、Create処理をcustomHooksとして切り出すための実装について書きます。

## これまで

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

https://qiita.com/ryosuketter/items/8b01d53d0609f78c2e0e

https://qiita.com/ryosuketter/items/59a05d4eb5c715287ded

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

https://qiita.com/ryosuketter/items/9e18295d78dc12a1a7f5

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

https://qiita.com/ryosuketter/items/442db08856093e6cfeea

## このシリーズの目次

- [このシリーズの方針](https://qiita.com/ryosuketter/items/1142c47913440369b660)
- customHooks を利用してロジックとUIを切り離すまで
    - [ロジックの作成](https://qiita.com/ryosuketter/items/67b168491fd5a186796e)👈 今回はコレ
    - [コンポーネント（UI）作成](https://qiita.com/ryosuketter/items/1dee37be7e4fe62ef6f0)
    - [不要なレンダリングが走らないための工夫（準備編）](https://qiita.com/ryosuketter/items/548dbd70ae835fd66dfb)
    - [不要なレンダリングが走らないための工夫（検証 & 改善編）](https://qiita.com/ryosuketter/items/4fb9b560b0d076491d91) 

## 最終的なゴール

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

![スクリーンショット 2021-10-18 17.27.41.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/14191/e8a9a2e1-2723-4fec-77ef-be3b9eefdd81.png)

### 目的（最終的なゴールを達成する）

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

## customHooks作成に必要なファイルを作成

```
components/
hooks/
└── useCreateForm.ts
pages/
```

## customHooks作成

### モジュールのimport

```ts:hooks/useCreateForms.ts
import { useState, ChangeEvent, FormEvent } from 'react'
import { useMutation } from '@apollo/client' // apolloを用いた通信処理
import { CREATE_USER } from '../queries/queries' // クエリコマンド
import { CreateUserMutation } from '../types/generated/graphql' // クエリコマンドに対応したデータ型
```

### カスタムフックの定義

```ts:hooks/useCreateForms.ts

export const useCreateForm = () => {}

```

### ２つのuseStateの定義

```ts:hooks/useCreateForms.ts

export const useCreateForm = () => {
  const [text, setText] = useState('')
  const [username, setUsername] = useState('')
}

```

### create処理を実装

前作ったやつをそのまま持ってきただけ

```ts:hooks/useCreateForms.ts

export const useCreateForm = () => {
  const [text, setText] = useState('')
  const [username, setUsername] = useState('')
  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]
            },
          },
        })
      },
    })
  }

```

これでユーザーの新規作成処理が実装できました。

### input要素を入力したときのイベント処理の実装

```ts:hooks/useCreateForms.ts

export const useCreateForm = () => {
  const [text, setText] = useState('')
  const [username, setUsername] = useState('')
  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 handleTextChange = (e: ChangeEvent<HTMLInputElement>) => {
    setText(e.target.value)
  }

```

引数はイベント要素を受け取るようにして、`setText()`だけする処理にしています。

### ユーザー名を変える処理の実装

```ts:hooks/useCreateForms.ts

export const useCreateForm = () => {
  const [text, setText] = useState('')
  const [username, setUsername] = useState('')
  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 handleTextChange = (e: ChangeEvent<HTMLInputElement>) => {
    setText(e.target.value)
  }
  const usernameChange = (e: ChangeEvent<HTMLInputElement>) => {
    setUsername(e.target.value)
  }

```

### submitしたときの処理の実装

```ts:hooks/useCreateForms.ts

export const useCreateForm = () => {
  const [text, setText] = useState('')
  const [username, setUsername] = useState('')
  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 handleTextChange = (e: ChangeEvent<HTMLInputElement>) => {
    setText(e.target.value)
  }
  const usernameChange = (e: ChangeEvent<HTMLInputElement>) => {
    setUsername(e.target.value)
  }
  const handleSubmit = async (e: FormEvent<HTMLFormElement>) => {
      e.preventDefault()
      try {
        await insert_users_one({
          variables: {
            name: username,
          },
        })
      } catch (err) {
        alert(err.message)
      }
      setUsername('')
    }

```

関数`handleSubmit`が実行されたら

非同期で、関数`insert_users_one`が実行されてユーザー情報を`{}`オブジェクトの中に入れて送っています。

非同期処理が成功・失敗にかかわらず終わったら、ユーザーネームを空文字をsetStateしています（初期化）。

## 最終的な実装結果

<details><summary>クリックすると実際のコードが見れます</summary><div>

```tsx
import { useState, ChangeEvent, FormEvent } from 'react'
import { useMutation } from '@apollo/client'
import { CREATE_USER } from '../queries/queries'
import { CreateUserMutation } from '../types/generated/graphql'

export const useCreateForm = () => {
  const [text, setText] = useState('')
  const [username, setUsername] = useState('')
  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 handleTextChange = (e: ChangeEvent<HTMLInputElement>) => {
    setText(e.target.value)
  }
  const usernameChange = (e: ChangeEvent<HTMLInputElement>) => {
    setUsername(e.target.value)
  }
  const printMsg = () => {
    console.log('Hello')
  }
  const handleSubmit = async (e: FormEvent<HTMLFormElement>) => {
    e.preventDefault()
    try {
      await insert_users_one({
        variables: {
          name: username,
        },
      })
    } catch (err) {
      alert(err.message)
    }
    setUsername('')
  }
  return {
    text,
    handleSubmit,
    username,
    usernameChange,
    printMsg,
    handleTextChange,
  }
}
```

</div></details>

最終的に作った関数をreturnしたり、検証用の関数なども入れてます。

## まとめ

今回は、Create処理をcustomHooksとして切り出すための実装について書きました。

## 次回

customHooksを利用してロジックとUIを切り離すまでのうち、ロジック作成はできたので、UI部分となるコンポーネント作成をしていきます。

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

https://qiita.com/ryosuketter/items/713474dbd009870e4103