【個人開発】学習記録一覧アプリ開発の振り返り (React、TypeScript、Jest、Firebase、Supabase、GitHub Actions)

# はじめに
学習記録一覧アプリ開発も大変でしたが、その分勉強になりました。

# 成果物
- 学習記録一覧
https://ts-study-record.web.app/

<img width="70%" alt="image" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3881019/500cfdbc-519d-0e3d-0aa8-a0dfd45c9b99.png">

<br/>
<br/>

<img width="70%" alt="image" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3881019/60fccfdc-9894-585f-1471-e6d7c20b25db.png">

- Qiita記事10本


### Qiitaへの記事投稿5本(特に苦労したこと)

[Jestで「mock」を作って、データを削除するテストをする](https://qiita.com/like-mountain/items/a81df2e95703468d1378)

[Jestで「spyOn」を使って、データを削除するテストをする](https://qiita.com/like-mountain/items/8089f5dbeb1cd051ce84)

[Firebaseをデプロイすると「Uncaught Error: supabaseUrl is required.」になる](https://qiita.com/like-mountain/items/202af2a80c459620be82)

[間違って上げたenvファイルをコミット履歴からも消去する](https://qiita.com/like-mountain/items/e91f854b064378ed3281)

# GitHub
https://github.com/box-7/ts-sample-vite  

# 気づき
(1)何をすれば要件を満たせるのか考える

削除機能のモック対応で、とても苦労した。
まず何をすれば要件を満たせるのか考える必要がある。

Qiitaで調べたコードを使っていたが、そもそも何をやりたいのか分かっていなかった。
モックデータを削除できる方法が分からず苦労したが、モックでは、実際にモックデータを削除する必要はなかった。

以下が対応方法。

- 削除機能の後に呼び出される、データ取得する部分をモック化する
(fetch()関数の中にある、supabaseからデータ取得する部分)
- 1回目の画面描画時に取得するデータと、2回目の削除後に取得するデータを変えることで、削除を表現できる


``` App.tsx
// データ取得関数
const fetchData = async () => {
  const getAllRecordMethod = async () => {
    // Supabaseを呼び出す処理 = GetAllRecords()をモック化する
    const todoRecord = await GetAllRecords();
    // 省略
  };
};

// 削除関数
const handleDelete = async (id: string) => {
  try {
    await RecordDelete(id);
    fetchData();
    // 省略
  } catch (error) {
    console.error(error);
  }
};
```

(2)事象をきちんと把握する

何となく大丈夫だろう、と考えず、最後まで確認する必要がある。
TypeScriptの型エラーの対応では、エラー確認に無駄に時間を費やしてしまった。
以下[1]の'RecordDelete'のコードで、下記エラーが出たのに、原因を探ることを怠ってしまった。

``` 
// [1]のエラー内容
型 'any[]' の引数を型 'void | Promise<void>' のパラメーターに割り当てることはできません。ts(2345)
"Testtest": Unknown word.cSpell
```

```test.tsx
// [1]RecordDelete → エラーになる
await waitFor(() => {
    jest.spyOn(recordLibDelete, 'RecordDelete')
    .mockResolvedValueOnce([
        new Record('10', 'Testtest10', 10),
        new Record('11', 'Testtest11', 11),
        new Record('12', 'Testtest12', 12),
      ]);
});

// 省略

// [2]GetAllRecords → エラーにならない
await waitFor(() => {
  jest
    .spyOn(recordLib, 'GetAllRecords')
    .mockResolvedValueOnce([
      new Record('5', 'Testtest5', 5),
      new Record('10', 'Testtest10', 10),
    ])
    .mockResolvedValueOnce([
      new Record('10', 'Testtest10', 10),
      new Record('11', 'Testtest11', 11),
      new Record('12', 'Testtest12', 12),
    ]);
});

```

'RecordDelete'の関数を確認すると、Promise<void>の箇所があった。
```
// [1]RecordDelete
export async function RecordDelete(id: string): Promise<void> {
```

'GetAllRecords'はエラーにならないので大丈夫だろうと思ってしまったが、元の型が異なるため、エラーが発生するのは当然だった。

```
// [2]GetAllRecords 
export async function GetAllRecords(): Promise<Record[]> {
```
確認不足で時間を無駄にしてしまった。
何となく大丈夫だろうと思わず、最後まで確認し、原因を追及することが大事だと感じた。


# 得たもの
- 情報収集力
- 課題解決力
- ドキュメントを読む習慣
- 設定ファイルを確認する習慣
(難しい課題だからこそ、力がつく)

# 使用技術
React
TypeScript
Firebase
Supabase
GitHub Actions
Jest
Prettier
ESLint
vite
ChakraUI

# 終わりに
気づきに書きましたが、まだ視野が狭いので、問題が起きるとそのポイントだけに気を取られてしまって、プログラム全体の流れに目を向けることができていないです。

視野を広げるには問題に対して自分で考えることを繰り返すしかないと思っています。
(無理なところは @Sicut_studyさん達にもアドバイスをいただきます)

また、詰めが甘く、最後まで確認しなかった箇所がエラー原因だったりもするので、最後までソースコードの流れを追うようにします。

課題解決の経験を積み、引き出しを増やせるよう、引き続きがんばります。