ToDoアプリをJavaScript/React/MySQLで作ろう【サンプルコード付き】

こんにちは!tenjiprogrammingです。

今回はToDoアプリの作成を解説します。

前回の四択クイズと同様に詳細すぎる解説はしません。4択クイズの解説記事はこちら↓

あわせて読みたい
JavaScript|写真付き4択クイズの作り方解説【コード有】 こんにちは!tenjiprogrammingです。今回はJavaScriptで写真付きの4択クイズを作成する方法を説明します。 サンプルコードも載せるので自分の環境でも書いて試してみて...

今回もコードは全て載せています。コピペでも模写でもいいので実際に自分の環境で作ってみてください!

目次

今回作るもの

今回作るToDoアプリは非常にシンプルなものです。まず実際に画面を紹介します。

ToDoアプリの画像
参考画像

タスクと納期を入力し追加ボタンを押すと、下にタスクが表示されます。
タスクを終えた際に完了ボタンを押すとそのタスクは非表示になります。

また今回データベースにMySQLを用いていますが、ローカルサーバで動かしています。
なので自分専用のToDoアプリです。

フロントエンドはJavaScriptとReactバックエンドはnodeで書いています。

では早速解説に入っていきましょう!

データベースの準備

MySQLの準備は以下の記事を参考にしてください。Progate様が公開しているページです。

windows向け(外部リンクに移ります。)

mac向け(外部リンクに移ります。)

MySQLサーバをローカルで動かす準備ができたらデータベーステーブルを作成します。

データベース名やテーブル名は任意で大丈夫です。

今回は、
データベース名 : todo
テーブル名 : tasks
としました!

テーブルのカラムは次のようにしました。

テーブル

一応create文も載せておきます。

SQL
CREATE TABLE tasks (
  id INT AUTO_INCREMENT PRIMARY KEY,
  title VARCHAR(255) NOT NULL,
  completed BOOLEAN DEFAULT false,
  deadline DATE
);

これで今回使うデータベースとテーブルの準備は終了です。
後ほどMySQLのユーザ名やパスワードは使うのでメモしておくのをおすすめします。

バックエンドの実装

次にバックエンドを実装します。

バックエンド実際にデータベースに値を登録したり、値を取得したりする部分です。

まずはコードの完成形を載せます。

JavaScript
// app.js
const express = require('express');
const cors = require('cors');
const mysql = require('mysql');

const app = express();
app.use(express.json());
app.use(cors());

const connection = mysql.createConnection({
  host: '127.0.0.1',  // localhostと書けばいいことが多い
  user: 'db_username', // データベースのユーザー名
  password: 'db_password', // データベースのパスワード
  database: 'todo', // データベース名
});

connection.connect((err) => {
  if (err) {
    console.error('MySQL接続エラー:', err);
  } else {
    console.log('MySQLに接続しました。');
  }
});

const port = 3001;

app.get('/api/tasks', (req, res) => {
  const getTasksQuery = 'SELECT * FROM tasks';
  connection.query(getTasksQuery, (err, results) => {
    if (err) {
      console.error('MySQLエラー:', err);
      return res.status(500).json({ error: 'タスクの取得に失敗しました。' });
    }
    res.status(200).json(results);
  });
});

app.post('/api/tasks', (req, res) => {
  const { title, deadline } = req.body;
  const insertQuery = 'INSERT INTO tasks (title, completed, deadline) VALUES (?, ?, ?)';
  connection.query(insertQuery, [title, false, deadline], (err, results) => {
    if (err) {
      console.error('MySQLエラー:', err);
      return res.status(500).json({ error: 'タスクの追加に失敗しました。' });
    }
    res.status(201).json({ id: results.insertId, title, completed: false, deadline });
  });
});

app.put('/api/tasks/:id/complete', (req, res) => {
  const taskId = req.params.id;
  const updateQuery = 'UPDATE tasks SET completed = ? WHERE id = ?';
  connection.query(updateQuery, [true, taskId], (err, results) => {
    if (err) {
      console.error('MySQLエラー:', err);
      return res.status(500).json({ error: 'タスクの完了状態の更新に失敗しました。' });
    }
    if (results.affectedRows === 0) {
      return res.status(404).json({ error: '指定されたタスクが見つかりません。' });
    }
    res.status(200).json({ id: taskId, completed: true });
  });
});

app.listen(port, () => {
  console.log(`サーバーがポート${port}で起動しました。`);
});

まずバックエンド用のディレクトリを用意してください。
名前はなんでも大丈夫です。そこでnode.jsのセットアップをします。

mkdir todo-backend
cd todo-backend
npm init -y

次に必要なパッケージをインストールします。

npm install express mysql body-parser cors --save

任意のファイルを作成し(今回の例だとapp.js)、そこに最初に紹介したコードを記述します。

バックエンドはこれで完成です。一応少しだけコードの解説をします。

解説

JavaScript
// app.js
const express = require('express');
const cors = require('cors');
const mysql = require('mysql');

const app = express();
app.use(express.json());
app.use(cors());

Expressアプリケーションを設定し、JSON形式のリクエストボディの解析とCORSの設定を行っています。
これらの設定を行うことで、フロントエンドとバックエンドが連携するための土台が整います。

JavaScript
const connection = mysql.createConnection({
  host: '127.0.0.1',  // localhostと書けばいいことが多い
  user: 'db_username', // データベースのユーザー名
  password: 'db_password', // データベースのパスワード
  database: 'todo', // データベース名
});

ここでデータベースの情報を記述します。ここが違うと上手くデータベースに接続できないので注意してください。
ホスト名は基本的にはlocalhostと書けば大丈夫ですが、homebrewを使ってMySQLを使用している場合127.0.0.1と記述しないとうまくできないことがあるので自分の環境に合わせて書き換えてください。

JavaScript
connection.connect((err) => {
  if (err) {
    console.error('MySQL接続エラー:', err);
  } else {
    console.log('MySQLに接続しました。');
  }
});

MySQLへの接続を行い失敗した場合のエラーハンドリングも実装しています。

JavaScript
const port = 3001;

バックエンドのサーバを立ち上げるポート番号を指定しています。
フロントエンド用のサーバと異なるポートであれば何番でも大丈夫です。

JavaScript
app.get('/api/tasks', (req, res) => {
  const getTasksQuery = 'SELECT * FROM tasks';
  connection.query(getTasksQuery, (err, results) => {
    if (err) {
      console.error('MySQLエラー:', err);
      return res.status(500).json({ error: 'タスクの取得に失敗しました。' });
    }
    res.status(200).json(results);
  });
});

このコードは、HTTP GETリクエストが /api/tasks に送信されたときにMySQLデータベースから全てのタスクを取得して、それをJSON形式で返すエンドポイントを定義しています。

データベースからタスクが欲しい時は、フロントエンドからapi/tasksにGETリクエストを送ることになります。

JavaScript
pp.post('/api/tasks', (req, res) => {
  const { title, deadline } = req.body;
  const insertQuery = 'INSERT INTO tasks (title, completed, deadline) VALUES (?, ?, ?)';
  connection.query(insertQuery, [title, false, deadline], (err, results) => {
    if (err) {
      console.error('MySQLエラー:', err);
      return res.status(500).json({ error: 'タスクの追加に失敗しました。' });
    }
    res.status(201).json({ id: results.insertId, title, completed: false, deadline });
  });
});

このコードは、HTTP POSTリクエストが /api/tasks に送信されたときにリクエストボディから取得したタスクの情報をMySQLデータベースに追加し、追加されたタスクの情報をJSON形式でクライアントに返すエンドポイントを定義しています。

タスクの追加を行うときは、フロントエンドからこのエンドポイントにPOSTリクエストを送信します。

JavaScript
app.put('/api/tasks/:id/complete', (req, res) => {
  const taskId = req.params.id;
  const updateQuery = 'UPDATE tasks SET completed = ? WHERE id = ?';
  connection.query(updateQuery, [true, taskId], (err, results) => {
    if (err) {
      console.error('MySQLエラー:', err);
      return res.status(500).json({ error: 'タスクの完了状態の更新に失敗しました。' });
    }
    if (results.affectedRows === 0) {
      return res.status(404).json({ error: '指定されたタスクが見つかりません。' });
    }
    res.status(200).json({ id: taskId, completed: true });
  });
});

このコードは、HTTP PUTリクエストが /api/tasks/:id/complete に送信されたときに指定されたタスクの完了状態をMySQLデータベースで更新し、更新後のタスクの情報をJSON形式でクライアントに返すエンドポイントを定義しています。

最初に見せた画面の完了ボタンを押した時このエンドポイントにPUTリクエストを送信します。

JavaScript
app.listen(port, () => {
  console.log(`サーバーがポート${port}で起動しました。`);
});

最後に先ほど指定したポート番号でサーバを起動させます。

バックエンドのコードの解説は以上です。次はフロントエンドを実装しましょう。

フロントエンドの実装

フロントエンド用のディレクトリはバックエンドと並列の場所に用意します。
用意する際はcreate-react-appで作成します。

npx create-react-app todo

これでreactの実行環境ができます。ディレクトリ構造は以下のとおりです。不要なファイルは削除しています。

ディレクトリ構造

コードを書くのはApp.jsAddTaskForm.jsです。

まずは完成形のコードです。

JavaScript
// src/App.js
import React, { useEffect, useState } from 'react';
import AddTaskForm from './components/AddTaskForm';

const App = () => {
  const [tasks, setTasks] = useState([]);

  const getIncompleteTasks = async () => {
    try {
      const response = await fetch('http://localhost:3001/api/tasks');
      const data = await response.json();
      setTasks(data);
    } catch (error) {
      console.error('エラー:', error);
    }
  };

  useEffect(() => {
    getIncompleteTasks();
  }, []);

  const handleTaskAdded = (newTask) => {
    setTasks([...tasks, newTask]);
  };

  const handleTaskCompletion = (taskId) => {
    fetch(`http://localhost:3001/api/tasks/${taskId}/complete`, {
      method: 'PUT',
      headers: {
        'Content-Type': 'application/json',
      },
    })
      .then((response) => response.json())
      .then((data) => {
        const updatedTasks = tasks.map((task) =>
          task.id === data.id ? { ...task, completed: true } : task
        );
        setTasks(updatedTasks);
        window.location.reload();

      })
      .catch((error) => console.error('エラー:', error));
  };

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

  return (
    <div>
      <h1>ToDoアプリ</h1>
      <AddTaskForm onTaskAdded={handleTaskAdded} />
      <ul>
        {filteredTasks.map((task) => (
          <li key={task.id}>
            {task.title} (納期: {task.deadline})
            <button onClick={() => handleTaskCompletion(task.id)}>完了</button>
          </li>
        ))}
      </ul>
    </div>
  );
};

export default App;
JavaScript
// src/components/AddTaskForm.js
import React, { useState } from 'react';

const AddTaskForm = ({ onTaskAdded }) => {
  const [title, setTitle] = useState('');
  const [deadline, setDeadline] = useState('');

  const handleTitleChange = (e) => {
    setTitle(e.target.value);
  };

  const handleDeadlineChange = (e) => {
    setDeadline(e.target.value);
  };

  const handleSubmit = (e) => {
    e.preventDefault();
    fetch('http://localhost:3001/api/tasks', {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
      },
      body: JSON.stringify({ title, deadline }),
    })
      .then((response) => response.json())
      .then((data) => {
        onTaskAdded(data);
        setTitle('');
        setDeadline('');
      })
      .catch((error) => console.error('エラー:', error));
  };

  return (
    <form onSubmit={handleSubmit}>
      <label>
        タスク:
        <input type="text" value={title} onChange={handleTitleChange} />
      </label>
      <label>
        納期:
        <input type="date" value={deadline} onChange={handleDeadlineChange} />
      </label>
      <button type="submit">追加</button>
    </form>
  );
};

export default AddTaskForm;

画面上にタスクを入力する欄と納期を選択するdate typeの入力フォームを用意します。

追加ボタンが押されたらPOSTリクエストを送信し、完了ボタンが押されたらPUTリクエストを送信します。

ポート番号はバックエンドのサーバが起動しているポート番号にしてください。

以上でフロントエンドのコードが完成です。

実行する

まずはMySQLサーバを起動します。ターミナルなどで

SQL
mysql -u 'ユーザ名' -p

を実行することでMySQLが起動します。

次にバックエンドのサーバを起動します。バックエンド用のディレクトリに移動したら、

node app.js

を実行します。

バックエンドターミナル

このような表示が出れば成功です。

最後にフロントエンドのサーバを起動します。フロントエンド用のディレクトリで

npm start

を実行します。正常に起動すると、ToDoアプリが開かれるはずです。

出るかもしれないエラー

MySQLサーバに接続できない系のエラーが出た場合

・MySQLのユーザ名などの接続情報が間違えている
・バージョンによるエラー

のどちらかであることが多いです。

前者の場合は正しい情報に修正すれば大丈夫です。

後者の場合はMySQLのターミナル上で

SQL
ALTER USER 'your_user'@'localhost' IDENTIFIED WITH mysql_native_password BY 'your_password';

を実行してください。(ユーザ名とパスワードは自分のものに変えてください。)

その後もう一度バックエンドを起動すればうまくいくはずです。

まとめ

今回はToDoリストの作り方を、サンプルコードを交えて解説しました。

このコードをアレンジして自分オリジナルのToDoリストを作ってみてください!

当ブログではJavaScriptの練習問題を無料で公開しているのでぜひ挑戦してみてください。

あわせて読みたい
【無料】初心者向けのJavaScriptの練習問題【解説有】 こんにちは! この記事ではJavaScriptの基礎を理解した方向けに、実際に手を動かして学べる練習問題をいくつか紹介します。JavaScriptはWeb開発に欠かせないプログラミ...
tenjiprogramming
20代エンジニア。
メインで使用している言語はJava/JavaScript/TyoeScript/react/C言語
AWSなどクラウド周りも経験あり。
楽しいをモットーに記事を書いています。
Noteではサンプルコード付きのゲームの作り方など様々な内容を公開しています。
そちらも是非ご覧ください!
よかったらシェアしてね!
  • URLをコピーしました!
  • URLをコピーしました!

コメント

コメントする

CAPTCHA


目次