React初学者がTodoアプリの画面に機能を追加する

## はじめに
JS/TSとReactの学習に取り組んで1週間が経ちました。プログラミング学習に本気で取り組み始めた非エンジニア、本業はYouTubeプロデューサーのShunです！今日はJISOUの課題1の続きを実装していきます。

頑張って発信していきますので、いいね！フォローよろしく願いします！

## 課題
課題1 学習記録アプリ
エンジニアとしての学習時間は1000時間が必要と言われています
そこで日々の学習の内容と学習時間を記録するアプリを作ることにしました

## MVP1 「ユーザーは学習記録を一覧でみることができる」
MVP1は前回取り組みました。
https://qiita.com/Shun_P/items/e7dce3f96cb227191bca

超簡単な画面だとは思いますが、僕にとっては難しく、いままでPythonしか触っていなかったのでなかなか分からず、慣れているPythonのStreamlitで実装しそうになりました。

## MVP2「ユーザーは新しい記録を登録できる」,MVP3「ユーザーは勉強時間の累計をみることができる」

MPV2を作ったあとMVP3も一連で実装したので、MVP2と3を一緒に作りました。

（MVP2で要求されている機能）
・学習内容の入力フォームをみることができる
・学習時間の入力フォームをみることができる
・学習時間の入力フォームは数字を入力できる
・現在のフォームに入力されている値を表示する(確認用)
・登録ボタンをみることができる
・登録ボタンをクリックするとrecordsに記録を追加できる
・登録をしたらフォームが初期化される(学習内容は空で学習時間は0が入る)
・全項目が入力されていないときにエラーが表示される(時間を空にしてもエラーがでるようにする)
・正しく入力されている場合登録ボタンを押すとエラーが消える

(MVP3で要求されている機能)
・記録した勉強の時間を合計した値をみることができる

### 書いたコード
```App.js
//課題で示されていた要件を満たした上で、「リセットボタン」、「小数点以下の学習時間入力」を追加した

import React, { useState } from "react";

function App() {
  const [records, setRecords] = useState([]);
  const [studyContent, setStudyContent] = useState("");
  const [studyTime, setStudyTime] = useState(0);
  const [error, setError] = useState("");

  const handleContentChange = (e) => {
    setStudyContent(e.target.value);
  };

  const handleTimeChange = (e) => {
    setStudyTime(parseFloat(e.target.value));
  };

  const handleSubmit = (e) => {
    e.preventDefault();
    if (studyContent === "" || studyTime <= 0) {
      setError("すべての項目を入力してください");
      return;
    }
    setRecords([...records, { content: studyContent, time: studyTime }]);
    setStudyContent("");
    setStudyTime(0);
    setError("");
  };

  const handleReset = () => {
    setRecords([]);
    setStudyContent("");
    setStudyTime(0);
    setError("");
  };

  const totalStudyTime = records.reduce(
    (total, record) => total + record.time,
    0
  );

  return (
    <div>
      <h1>学習記録アプリ</h1>
      <form onSubmit={handleSubmit}>
        <div>
          <label>学習内容：</label>
          <input
            type="text"
            step="0.1"
            value={studyContent}
            onChange={handleContentChange}
          />
        </div>
        <div>
          <label>学習時間：</label>
          <input type="number" value={studyTime} onChange={handleTimeChange} />
        </div>
        <button type="submit">登録</button>
      </form>
      {error && <p style={{ color: "red" }}>{error}</p>}
      <h2>現在の入力内容</h2>
      <p>入力されている学習内容: {studyContent}</p>
      <p>入力されている学習時間: {studyTime}時間</p>
      <h2>記録一覧</h2>
      <ul>
        {records.map((record, index) => (
          <li key={index}>
            {record.content}: {record.time}時間
          </li>
        ))}
      </ul>
      <h2>合計学習時間</h2>
      <p>{totalStudyTime}/1000 (h)</p>
      <button onClick={handleReset}>リセット</button>
    </div>
  );
}

export default App;
```

### 画面のプレビュー

![Monosnap App.js - nodebox - CodeSandbox - Google Chrome 2024-05-21 21.34.21.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3502843/85239483-dc66-e34e-4551-e25f8a26d785.png)

### ちょっと加えた処理
MVP2と3で要求されていた機能を実装したところ、リセットボタンがあった方が便利だなと感じたのと、学習時間は1時間単位ではなく、30分などもあるので、0.1時間単位でも入力できる様にしました。


## 感想

超簡単な画面だとは思いますが、僕にとっては難しく、いままでPythonしか触っていなかったのでなかなか分からず、慣れているPythonだったらどう実装できるか調べてしまい、使ってみたかったStreamlitでもコードを書いてしまいました。


## おまけ

```app.py
import streamlit as st

def main():
    st.title("学習記録アプリ")

    if "records" not in st.session_state:
        st.session_state.records = []

    study_content = st.text_input("学習内容")
    study_time = st.number_input("学習時間", min_value=0.0, step=0.1)
    error = st.empty()

    if st.button("登録"):
        if study_content == "" or study_time <= 0:
            error.error("すべての項目を入力してください")
        else:
            st.session_state.records.append({"content": study_content, "time": study_time})
            study_content = ""
            study_time = 0.0
            error.empty()

    st.subheader("現在の入力内容")
    st.write(f"入力されている学習内容: {study_content}")
    st.write(f"入力されている学習時間: {study_time}時間")

    st.subheader("記録一覧")
    for record in st.session_state.records:
        st.write(f"{record['content']}: {record['time']}時間")

    total_study_time = sum(record["time"] for record in st.session_state.records)
    st.subheader("合計学習時間")
    st.write(f"{total_study_time}時間/1000 (h)")

    if st.button("リセット"):
        st.session_state.records = []
        study_content = ""
        study_time = 0.0
        error.empty()

if __name__ == "__main__":
    main()
```

![Monosnap main · Streamlit - Google Chrome 2024-05-21 21.46.54.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3502843/eaf1eab9-eee7-ae26-715f-af96e522091a.png)


今回はここまでです。いいね！よろしく願いします！