⭐️

Reactロードマップ(2) ~TODOアプリ編~

2024/03/15に公開

はじめに

前回に続き今回はQiitaで見たReactロードマップに沿って簡単なTODOを実装していこうと思います!以下参考にさせていただいた記事と前回の自分の記事です。
https://qiita.com/Sicut_study/items/78910cd8d103b8d45a73
https://zenn.dev/hirohiro_sys/articles/6663f5cd880320

ざっと実装手順

①JSX構造定義
②Reactでの実装を意識したモックに変更
③タスクの追加機能実装
④タスクの削除機能実装
⑤タスクの完了機能実装(追加+削除的な感じ)
⑥タスクの戻す機能(ほぼ完了と同じ)
⑦追加機能実装(日付けやメッセージ)
⑧コードがエグ長くて見づらいのでコンポーネント分割

実装してみた

  • gif(見づらくてすみません...)
    React-Todo
    基本的にudemyの1つ目のコースで作ったTODOと構造は同じです。あとは追加で日付を扱えるようにしたり、タスクが何もない時はその旨を伝えるメッセージを表示するようにしました。スタイルというスタイルはほぼ当たってません(笑)。以下コードになります。

  • tree

src
├── App.css
├── App.js
├── App.test.js
├── components
│   ├── completeTodos.jsx
│   ├── incompleteTodos.jsx
│   └── inputTodo.jsx
  • App.jsx
import { useState } from "react";
import { CompleteTodos } from "./components/completeTodos";
import { IncompleteTodos } from "./components/incompleteTodos";
import { InputTodo } from "./components/inputTodo";
import "./App.css";

function App() {
  const [todoText, setTodoText] = useState("");
  const [deadLine, setDeadLine] = useState("");
  const [incompleteTodos, setIncompleteTodos] = useState([]);
  const [completeTodos, setcompleteTodos] = useState([]);
  const onChangeTodoText = (e) => setTodoText(e.target.value);
  const onChangeDeadLine = (e) => setDeadLine(e.target.value);
  // タスクの追加
  const onClickAdd = () => {
    if (todoText === "" || deadLine === "") return;
    const newTodos = [...incompleteTodos, { text: todoText, date: deadLine }];
    setIncompleteTodos(newTodos);
    setTodoText("");
    setDeadLine("");
  };
  // 削除
  const onClickDelete = (index) => {
    const newTodos = [...incompleteTodos];
    newTodos.splice(index, 1);
    setIncompleteTodos(newTodos);
  };
  // 完了
  const onClickComplete = (index) => {
    const newIncompleteTodos = [...incompleteTodos];
    newIncompleteTodos.splice(index, 1);
    setIncompleteTodos(newIncompleteTodos);
    const newCompleteTodos = [...completeTodos, incompleteTodos[index]];
    setcompleteTodos(newCompleteTodos);
  };
  // 戻す
  const onClickBack = (index) => {
    const newCompleteTodos = [...completeTodos];
    newCompleteTodos.splice(index, 1);
    setcompleteTodos(newCompleteTodos);
    const newIncompleteTodos = [...incompleteTodos, completeTodos[index]];
    setIncompleteTodos(newIncompleteTodos);
  };
  return (
    <>
      <h1><i>シンプルなTODOアプリ</i></h1>
      <InputTodo todoText={todoText} deadLine={deadLine} onChange={onChangeTodoText} onChangeDate={onChangeDeadLine} onClick={onClickAdd}/>
      <IncompleteTodos todos={incompleteTodos} onClickComplete={onClickComplete} onClickDelete={onClickDelete}/>
      <CompleteTodos todos={completeTodos} onClickBack={onClickBack}/>
    </>
  );
}

export default App;

  • inputTodo.jsx
// 入力欄
export const InputTodo = (props) => {
  const { todoText, deadLine, onChange, onChangeDate, onClick } = props;
  return (
    <div>
      <input placeholder="TODOを入力" value={todoText} onChange={onChange} />
      <input type="date" value={deadLine} onChange={onChangeDate} />
      <button onClick={onClick}>追加</button>
    </div>
  );
};
  • incompleteTodos.jsx
// 未完了エリア
export const IncompleteTodos = (props) => {
  const { todos, onClickComplete, onClickDelete } = props;
  return (
    <div>
      <p className="title">未完了のTODO一覧</p>
      {todos.length === 0 ? (
        <p>未完了のタスクはありません。</p>
      ) : (
        <ul>
          {todos.map((todo, index) => (
            <li key={todo}>
              <div className="list-row">
                <p className="text-margin">{todo.text}</p>
                <p>(締切: {todo.date})</p>
                <button onClick={() => onClickComplete(index)}>完了</button>
                <button onClick={() => onClickDelete(index)}>削除</button>
              </div>
            </li>
          ))}
        </ul>
      )}
    </div>
  );
};
  • completeTodos.jsx
// 完了エリア
export const CompleteTodos = (props) => {
  const { todos, onClickBack } = props;
  return (
    <div>
      <p className="title">完了のTODO一覧</p>
      {todos.length === 0 ? (
        <p>完了済みのタスクはありません。</p>
      ) : (
        <ul>
          {todos.map((todo, index) => (
            <li key={todo}>
              <div className="list-row">
                <p className="text-margin">{todo.text}</p>
                <button onClick={() => onClickBack(index)}>戻す</button>
              </div>
            </li>
          ))}
        </ul>
      )}
    </div>
  );
};
  • App.css
/* お気持ちスタイル */
.title {
    font-weight: bold;
}
.list-row {
    display: flex;
    align-items: center; 
}
.text-margin {
    margin-right: 10px;
}

悩んだところ

正直udemyの一つ目のコースでTODOを作ってた際にほとんどコードを覚えてしまったので(それ意味あるんか)特に悩んだところはなかったです。強いて言うなら日付けを扱えるようにする箇所で少し躓きましたが、既存のフォームの実装とほとんど同じだったのであまり時間はかかりませんでした。

今後試したいこと

  • TypeScriptで実装
  • chakura UIとかでのスタイリング
  • テスト
  • バックエンドとの連携
  • 機能追加

等々あげたらキリがないですが、このあたりは次回の簡単な個人開発のフェーズでやろうと思います💪

おわりに

今回はTODOを作ってみました。ロードマップに沿って次はオリジナルのアプリを作ってみようと思います!

Discussion