React学習記録

【React入門】シンプルなTODOアプリの作り方 – 中編【タスク追加機能の実装】

yuya_react

はじめに

こんにちは、優也@月収100万を目指して月100時間勉強する男です。

前回の記事「【React入門】TODOアプリの作り方 – 前編」では、プロジェクトのセットアップとタスク一覧の表示機能を実装しました。今回の中編では、タスクの追加機能を実装していきます。

入力フォームを作成し、新しいタスクを追加できるようにします。ユーザーが実際に操作できるTODOアプリに一歩近づきます!

前回のおさらい

前編で実装した機能:

  • ✅ プロジェクトのセットアップ
  • ✅ 初期データの準備
  • ✅ タスク一覧の表示
  • ✅ 未完了/完了タスクの分離表示

今回実装する機能

中編(今回)で実装する機能:

  • ✅ タスク入力フォームの作成
  • ✅ タスク追加機能
  • ✅ 入力後のクリア処理
  • ✅ 空文字チェック

前回のコードを準備

まず、前回完成したコードから始めます。

import { useState } from 'react';

function App() {
  const [tasks, setTasks] = useState([
    { id: 1, taskName: '朝食を作る', completed: false },
    { id: 2, taskName: '買い物に行く', completed: false },
    { id: 3, taskName: 'ブログを書く', completed: true }
  ]);

  const incompleteTasks = tasks.filter((task) => !task.completed);
  const completedTasks = tasks.filter((task) => task.completed);

  return (
    <div>
      <h1>TODO APP</h1>
      
      <h2>未完了タスク</h2>
      {incompleteTasks.length === 0 ? (
        <p>未完了タスクはありません</p>
      ) : (
        <table border="1">
          <thead>
            <tr>
              <th>タスク名</th>
            </tr>
          </thead>
          <tbody>
            {incompleteTasks.map((task) => (
              <tr key={task.id}>
                <td>{task.taskName}</td>
              </tr>
            ))}
          </tbody>
        </table>
      )}

      <h2>完了タスク</h2>
      {completedTasks.length === 0 ? (
        <p>完了タスクはありません</p>
      ) : (
        <table border="1">
          <thead>
            <tr>
              <th>タスク名</th>
            </tr>
          </thead>
          <tbody>
            {completedTasks.map((task) => (
              <tr key={task.id}>
                <td>{task.taskName}</td>
              </tr>
            ))}
          </tbody>
        </table>
      )}
    </div>
  );
}

export default App;

ここから機能を追加していきます。

入力フォームのstateを追加

タスク名を入力するためのstateを追加します。

import { useState } from 'react';

function App() {
  const [inputTask, setInputTask] = useState('');
  const [tasks, setTasks] = useState([
    { id: 1, taskName: '朝食を作る', completed: false },
    { id: 2, taskName: '買い物に行く', completed: false },
    { id: 3, taskName: 'ブログを書く', completed: true }
  ]);

 // 以下省略...

ポイント

  • inputTask: 入力中のタスク名を保持
  • 初期値は空文字''

入力フォームを作成

タスクを入力するためのフォームを追加します。

return (
  <div>
    <h1>TODO APP</h1>
    
    <h2>タスクの追加</h2>
    <div>
      <input 
        type="text"
        value={inputTask}
        onChange={(e) => setInputTask(e.target.value)}
        placeholder="タスクを入力"
      />
      <button type="button">追加</button>
    </div>

    <h2>未完了タスク</h2>
    /* 以下省略 */

コードの説明

input要素:

<input 
  type="text"
  value={inputTask}
  onChange={(e) => setInputTask(e.target.value)}
  placeholder="タスクを入力"
/>
  • value={inputTask}: stateと連動
  • onChange: 入力時にstateを更新
  • e.target.value: 入力された値

これで、文字を入力するとリアルタイムでinputTaskが更新されます。

タスク追加機能を実装

新しいタスクを追加する関数を作成します。

import { useState } from 'react';

function App() {
  const [inputTask, setInputTask] = useState('');
  const [tasks, setTasks] = useState([
    { id: 1, taskName: '朝食を作る', completed: false },
    { id: 2, taskName: '買い物に行く', completed: false },
    { id: 3, taskName: 'ブログを書く', completed: true }
  ]);

  const addTask = () => {
    const newId = tasks.length === 0 
      ? 1 
      : Math.max(...tasks.map(task => task.id)) + 1;
    
    const newTask = {
      id: newId,
      taskName: inputTask,
      completed: false
    };

    setTasks([...tasks, newTask]);
    setInputTask('');
  };

// 以下省略...

コードの説明

ID生成:

const newId = tasks.length === 0 
  ? 1 
  : Math.max(...tasks.map(task => task.id)) + 1;
  • tasks.length === 0: タスクが0件なら1
  • Math.max(...tasks.map(task => task.id)): 最大IDを取得
  • + 1: 最大ID + 1

新しいタスクを作成:

const newTask = {
  id: newId,
  taskName: inputTask,
  completed: false
};

配列に追加:

setTasks([...tasks, newTask]);

スプレッド構文で既存のタスクを展開し、新しいタスクを追加

入力欄をクリア:

setInputTask('');

ボタンにイベントを設定

追加ボタンをクリックした時にaddTaskを実行します。

<button type="button" onClick={addTask}>追加</button>

これで、ボタンをクリックすると新しいタスクが追加されます!

空文字チェックを追加

空のタスクが追加されないようにチェックを追加します。

const addTask = () => {
  if (inputTask.trim() === '') return;

  const newId = tasks.length === 0 
    ? 1 
    : Math.max(...tasks.map(task => task.id)) + 1;
  
  const newTask = {
    id: newId,
    taskName: inputTask,
    completed: false
  };

  setTasks([...tasks, newTask]);
  setInputTask('');
};

コードの説明

if (inputTask.trim() === '') return;
  • trim(): 前後の空白を削除
  • === '': 空文字の場合
  • return: 関数を終了(何もしない)

これで、空文字やスペースのみの入力は追加されません。

Enterキーでも追加できるようにする

入力欄でEnterキーを押した時も追加できるようにします。

<input 
  type="text"
  value={inputTask}
  onChange={(e) => setInputTask(e.target.value)}
  onKeyDown={(e) => {
    if (e.key === 'Enter') {
      addTask();
    }
  }}
  placeholder="タスクを入力"
/>

コードの説明

onKeyDown={(e) => {
  if (e.key === 'Enter') {
    addTask();
  }
}}
  • onKeyDown: キーが押された時のイベント
  • e.key === 'Enter': Enterキーの場合
  • addTask(): タスク追加関数を実行

現在のコード全体

ここまでで完成したコードは以下の通りです:

import { useState } from 'react';

function App() {
  const [inputTask, setInputTask] = useState('');
  const [tasks, setTasks] = useState([
    { id: 1, taskName: '朝食を作る', completed: false },
    { id: 2, taskName: '買い物に行く', completed: false },
    { id: 3, taskName: 'ブログを書く', completed: true }
  ]);

  const addTask = () => {
    if (inputTask.trim() === '') return;

    const newId = tasks.length === 0 
      ? 1 
      : Math.max(...tasks.map(task => task.id)) + 1;
    
    const newTask = {
      id: newId,
      taskName: inputTask,
      completed: false
    };

    setTasks([...tasks, newTask]);
    setInputTask('');
  };

  const incompleteTasks = tasks.filter((task) => !task.completed);
  const completedTasks = tasks.filter((task) => task.completed);

  return (
    <div>
      <h1>TODO APP</h1>
      
      <h2>タスクの追加</h2>
      <div>
        <input 
          type="text"
          value={inputTask}
          onChange={(e) => setInputTask(e.target.value)}
          onKeyDown={(e) => {
            if (e.key === 'Enter') {
              addTask();
            }
          }}
          placeholder="タスクを入力"
        />
        <button type="button" onClick={addTask}>追加</button>
      </div>

      <h2>未完了タスク</h2>
      {incompleteTasks.length === 0 ? (
        <p>未完了タスクはありません</p>
      ) : (
        <table border="1">
          <thead>
            <tr>
              <th>タスク名</th>
            </tr>
          </thead>
          <tbody>
            {incompleteTasks.map((task) => (
              <tr key={task.id}>
                <td>{task.taskName}</td>
              </tr>
            ))}
          </tbody>
        </table>
      )}

      <h2>完了タスク</h2>
      {completedTasks.length === 0 ? (
        <p>完了タスクはありません</p>
      ) : (
        <table border="1">
          <thead>
            <tr>
              <th>タスク名</th>
            </tr>
          </thead>
          <tbody>
            {completedTasks.map((task) => (
              <tr key={task.id}>
                <td>{task.taskName}</td>
              </tr>
            ))}
          </tbody>
        </table>
      )}
    </div>
  );
}

export default App;

コード行数: 約80行

動作確認

ブラウザで以下を確認してください:

  1. ✅ 入力欄に文字を入力できる
  2. ✅ 「追加」ボタンをクリックすると未完了タスクに追加される
  3. ✅ 追加後、入力欄が自動的にクリアされる
  4. ✅ 空文字は追加されない
  5. ✅ Enterキーでも追加できる

すべて動作していれば成功です!

まとめ

TODOアプリの中編として、タスク追加機能を実装しました。

今回実装した機能:

  • ✅ タスク入力フォームの作成
  • ✅ タスク追加機能
  • ✅ 入力後のクリア処理
  • ✅ 空文字チェック
  • ✅ Enterキーでの追加

使った技術:

  • useState: 入力中のタスクを管理
  • onChange: 入力の監視
  • onClick: ボタンクリック
  • onKeyDown: Enterキー対応
  • スプレッド構文: 配列への追加
  • trim(): 空白除去

次回の後編では、タスクの削除機能と完了機能を実装します。これでTODOアプリが完成します!


この記事のシリーズ:

  1. 【React入門】未経験からReactを学んで最初のTODOアプリを作った話
  2. 【2025年版】Viteで始めるReact開発環境の構築方法
  3. 【React基礎】useStateとは?初心者向けに分かりやすく解説
  4. 【React】イベントハンドラの書き方完全ガイド
  5. 【React】map関数でリストをレンダリングする方法
  6. 【React入門】TODOアプリの作り方 – 前編
  7. 【React入門】TODOアプリの作り方 – 中編 ← 今ここ
  8. 【React入門】TODOアプリの作り方 – 後編(次回)

あとがき

月100時間の学習時間の中で、今月はReactに集中して取り組んでいます。Instagramでは、フリーランスとして月収100万円を目指す道のりを、リアルタイムで発信していきますので、ぜひフォローしてください!

Instagram: @hooded_yuya_100man

ABOUT ME
記事URLをコピーしました