1
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?

TypeScript で学習時間を登録するアプリをつくる

Posted at

はじめに

JISOU というコミュニティで React + TypeScript の勉強をしています。取り組んだ課題は学習時間を登録するアプリです。

  • 登録した学習時間

image.png

  • 登録ダイアログ

image.png

  • 編集ダイアログ

image.png

  • 課題でチャレンジした技術
    • バックエンドは Supabase で構築
    • React を TypeScript で作成
    • ChakraUI でレイアウトを作成
    • useState で管理する学習時間に新しい登録を追加、登録済の学習時間の更新
    • react-hook-form を使った submit コールバック、バリデーション

工夫してみたところ

登録ダイアログ、編集ダイアログをコンポーネントにしてみました。課題のなかでは無理にコンポーネントしなくても良いとアドバイスがありましたが、やってみたい欲に駆られてチャレンジしてみました。工夫してみたところは

  • 親のステートを子コンポーネントで更新する
  • 登録ダイアログと更新ダイアログを分ける

です。

React 始めたばかりのひとが面白がってコンポーネント分けてるだけなので正確性は低いよ!

親のステートを子コンポーネントで更新するなら?

Gemini に問い合わせてみたところ、set 関数を子コンポーネントにわたすのがいいと回答を得ました。本当にいいかはさておき、確かに set 関数を子コンポーネントで操作すると良さそうです。

TypeScript では set 関数を引数に指定するにも型が必要になり Dispatch<SetStateAction<Record[]>> と書くのが新しい気付きでした。

type Props = {
  handleNewRecord: Dispatch<SetStateAction<Record[]>>
}

export default function NewRecordFormDialog({ handleNewRecord } : Props) {
  const onSubmit = handleSubmit(async (data) => {
    const newRecord : Record = await insertRecord(data.title, data.time)
    handleNewRecord((prevRecords : Record[]) => [newRecord, ...prevRecords])

    setOpen(false)
    reset();
  })

  return (
    <>
      <form onSubmit={onSubmit}>
        ...
      </form>
    <>
  )
}

登録ダイアログと更新ダイアログは同じコンポーネントにする?

登録ダイアログと更新ダイアログのコンポーネントは分けることにしました。理由は更新ダイアログでは対象データを渡す必要があり、かつ supabase の更新 API を使う必要がありました。煩雑になるので登録ダイアログと更新ダイアログは分けて考えることにしました。

type Props = {
  id: string,
  title: string,
  time: number,
  handleUpdateRecord: Dispatch<SetStateAction<Record[]>>
}

export default function UpdateRecordFormDialog({ id, title, time, handleUpdateRecord } : Props) {
  const onSubmit = handleSubmit(async (data) => {
    const updatedRecord : Record = await updateRecord(id, data.title, data.time)

    handleUpdateRecord((prevRecords : Record[]) => {
      return prevRecords.map((prevRecord) => prevRecord.id === updatedRecord.id ? updatedRecord : prevRecord)
    })

    setOpen(false)
    reset();
  })

  return (
    <>
      <form onSubmit={onSubmit}>
        ...
      </form>
    <>
  )
}

登録ダイアログと更新ダイアログをひとつのコンポーネントとするなら Props で ? を活用すると良さそうです。ただ、idtitletime はすべて指定する必要がありヒューマンエラーに気づきづらいかもです。

type Props = {
  id?: string,
  title?: string,
  time?: number,
  handleUpdateRecord: Dispatch<SetStateAction<Record[]>>
}

次に向けて

いま登録した学習時間は降順としたいので supabase でも降順(新しいものから古いものへ)で取得しています。新しく登録したものは配列の先頭に挿入しています。いざ昇順に変えるとなったときに考慮すべき点が増えるのであまりよい構造とは言えません。

ただ、全レコードをフェッチするよりメモリ効率は良さそうです :thinking: どちらがパフォーマンスが良いか要検討ですね。

JISOUのメンバー募集中!

プログラミングコーチングJISOUでは、新たなメンバーを募集しています。
日本一のアウトプットコミュニティでキャリアアップしませんか?
興味のある方は、ぜひホームページをのぞいてみてください!
▼▼▼

1
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
1
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?