はじめに
以前作った下記の学習記録アプリをリニューアルしました。
https://qiita.com/hiroshi_buildup/items/37722eceba387fb18dc4
TypeScript化、Chakra UIでのデザイン、react-hook-formでのフォーム実装、編集機能の追加、GitHub ActionsでのCI/CDまで行いました。
機能の一覧
- 学習記録の一覧表示
- ローディング表示
- 学習記録の新規登録(モーダル)
- 学習内容・学習時間の入力
- 未入力、学習時間が0未満の場合はエラー表示
- 学習記録の編集(登録と同じモーダルを再利用)
- 編集ボタンを押すと、その行の内容がフォームに入る
- 別の行を選ぶと内容が切り替わり、新規登録なら空になる
- 学習記録の削除
- Pushで自動的にビルド・テスト・デプロイが走るCI/CD
使用技術
| 分類 | 技術 |
|---|---|
| フロントエンド | React / TypeScript / Vite |
| UIライブラリ | Chakra UI v2 |
| フォーム | react-hook-form |
| バックエンド | Supabase |
| テスト | React Testing Library |
| CI/CD | GitHub Actions |
Chakra UIとreact-hook-formを使用して改修
Modal、Table、Spinner、FormControlなどを使ってデザインしました。
react-hook-formでバリデーション
register にルールを渡すだけで、必須と最小値のチェックができました。
<Input
type="number"
{...register("time", {
required: "時間の入力は必須です",
min: { value: 0, message: "時間は0以上である必要があります" },
valueAsNumber: true,
})}
/>
登録と編集でモーダルを使い回した
コンポーネントを分けず、1つのモーダルで新規と編集を切り替えています。
編集対象のrecordがnullなら新規、値があれば編集、という判断をif文で行っています。
useEffect(() => {
if (!isOpen) return;
if (record) {
reset({ title: record.title, time: record.time });
} else {
reset({ title: "", time: "" });
}
}, [isOpen, record, reset]);
保存時も同様に、recordの有無でupdateRecordとcreateRecordを分けます。
工夫したこと
- 新規と編集でモーダルを1つにして、重複コードを減らした
- PushするとGitHub Actionsでビルド・テスト・デプロイが走るようにした
気づき
1. テストが異常に遅い・固まる → 原因はiCloud
プロジェクトを~/Desktopに置いていて、Jestの実行に数十分かかる、または固まる状態になりました。
デスクトップがiCloud Driveの同期対象になっており大量のファイルが同期の影響を受けていたのが原因でした。フォルダを作成して移動すると、数秒で終わるようになりました。
2. 本番で supabaseUrl is required が出た
.envはGitHubに上げていないため、CIのビルドに環境変数が入らず、本番で空になっていました。
GitHub Secretsに登録し、ワークフローのビルドステップに渡して解決しました。
- run: npm run build
env:
VITE_SUPABASE_URL: ${{ secrets.VITE_SUPABASE_URL }}
VITE_SUPABASE_ANON_KEY: ${{ secrets.VITE_SUPABASE_ANON_KEY }}
3. 編集→新規でフォームが空にならない
reset({ title: "", time: undefined }) では、時間だけ前の値が残りました。
undefinedではなく空文字を渡すと直りました。
まとめ
テストコードとCI/CDの実装にかなり苦戦しました。
これまで全く触ってこなかった領域なので、どこから進めればよいかわからず何度も手が止まりました。
公式ドキュメントを読みつつAIにも説明してもらいながら確実に自分の技術として定着するように意識しました。
引き続きアウトプットしながら学習を続けます。
