学習管理アプリ作成の所感とReact開発において意識するべきこと

## はじめに
JISOUの課題となっている「学習管理アプリ」をReactで作成しました。
小規模なアプリではありますが、React開発で意識するべきことをいくつか発見しましたのでご紹介いたします。

## 作成したアプリ
TODOアプリに毛が生えたようなもの。
学習内容と学習時間を登録し、登録内容を一覧表示する。

![画面収録 2024-07-29 20.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3832049/29176995-66c9-b521-8377-2bc574eb1d58.gif)

:::note info
Point
・インプット項目に入力した値は画面上にリアルタイムで表示させる。
・エラー（学習内容/時間が空白）時はエラーメッセージを表示する。
・学習合計時間を表示する。
:::

## 開発時に特に意識したポイント

 - #### 繰り返し項目には一意のキーを指定する
React開発においてmapを用いて繰り返しを表現する際は、繰り返しを行うタグに対してkey属性を指定する必要があります。
ここで、今回のアプリでは学習内容をそのままkeyに設定してしまうと、同じ学習内容を設定した場合にkeyが一意ではなくなってしまいます。
そこで、以下のようにkeyを指定することでkeyの重複を防いでいます。
```react:RecordsList.jsx（ul部分のみ抜粋）
            <ul style={{ margin: 0 }}>
                {records.map(({ title, time }, index) => (
                    <li key={`${title}_${index}`}>
                        {`${title} ${time}時間`}
                    </li>
                ))}
            </ul>
```
mapの第２引数は配列のインデックス番号となるため、上記のように学習内容と配列のインデックス番号を連結してkeyとすることで、学習内容が重複していても一意性は担保されます。
React開発においてmapでの繰り返しは度々実装しそうなので、このテクニックは他でも使えるのでは？と思いました。
<br/>

 - #### 「登録」ボタンの押下時だけでなく、Enterキーを押下しても登録を行うようにする
これは要件にもよりますが、今回のようなアプリの場合は「登録」ボタンを押下するだけでなく、入力項目に値を設定している間にEnterキーを押下することで登録処理を走らせるようにすればより使いやすいのではと思い、実装してみました。
```react:Form.jsx（抜粋）
        <form onSubmit={regist}>
            学習内容
            <input type="input" value={titleText} onChange={e => setTitleText(e.target.value)} />
            <br />
            学習時間
            <input type="number" value={timeText} onChange={e => setTimeText(e.target.value)} />時間
            <br />
            入力されている学習内容：{titleText}
            <br />
            入力されている時間：{timeText}時間
            <br />
            <button>登録</button>
            <div style={{ color: "red" }}>
                {error}
            </div>
        </form>
```

```react:App.jsx（抜粋）
  const regist = (e) => {
    e.preventDefault();
    // チェック
    if (!titleText || !timeText) {
      setError("入力されていない項目があります。");
      return;
    }

    // 登録
    setRecords(records => [...records, {
      title: titleText,
      time: timeText
    }]);

    // 初期化
    setTitleText("");
    setTimeText("");
    setError("");
  }
```
まず、入力項目の部分をformで囲ったうえでコンポーネントとして切り出します。
（この時、buttonタグではなくformのonSubmit属性に関数を設定するのがポイント。）
また、関数の定義では最初にe.preventDefault()を入れることでブラウザのデフォルトのサブミット処理を防いでいます（これを入れないとサブミット時に画面が再描画されてしまう）。

formで囲われた入力項目上でEnterキーを押下するとサブミット処理が実行される、というのがブラウザのデフォルトの挙動となるため、これでやりたいことは実現可能となります（もしかしたらブラウザによってはできないかも…）。
この方法についても今後のアプリ開発で使える手法かと思います！
<br/>

 - #### 合計時間はreduceメソッドを使って出力する
アプリの中で学習時間の合計を出力していますが、こちらはstateではなくJavaScriptのreduceメソッドを使って出力しています。

```react:RecordsList.jsx（合計時間のみ抜粋）
合計時間：{records.reduce((accu, { time }) => accu + parseInt(time), 0)}/1000(h)
```
reduceメソッドは、配列データを集約して一つの値を返却する関数ですが、
こうすることで合計時間のstateを保持する必要がなくなるので効率的になります！
（加算する際にparseIntメソッドで学習時間を数値に変換しています。
これを入れないと文字列同士の加算になってしまう（例：10 + 5 = 105）。）

## React開発を初めてやってみた感想
以前Vueを使った経験があったので、割とすんなり理解できた印象はあります。
ただ、このぐらいのアプリでは意識する必要はないのですが、中規模〜大規模のアプリになってくるとパフォーマンスも意識する必要があると思うので、そうなると結構難しそうだな、と思いました（いつ再レンダリングが走るのか、とか処理が重いせいで画面がちらつくとか…）。
とはいえ、最初のアプリとしては楽しく作れましたし、React開発はやってみてすごく面白いな、と思いました！！
これからもこの調子でアウトプットしていきたいと思います！

## JISOUのメンバー募集中
プログラミングコーチングJISOUではメンバーを募集しています。
日本一のアウトプットコミュニティでキャリアアップしませんか？

気になる方はぜひHPからライン登録お願いします👇

https://projisou.jp/