mkdir mern-todo-app
cd mern-todo-app
npm init -y
1.2. Install Dependencies
npm install express mongoose body-parser cors
1.3.設定伺服器
建立一個 index.js 檔案來設定 Express 伺服器並連接到 MongoDB。
// index.js
const express = require('express');
const mongoose = require('mongoose');
const bodyParser = require('body-parser');
const cors = require('cors');
const app = express();
const PORT = process.env.PORT || 5000;
mongoose.connect('mongodb://localhost:27017/todoapp', {
  useNewUrlParser: true,
  useUnifiedTopology: true,
});
const db = mongoose.connection;
db.on('error', console.error.bind(console, 'connection error:'));
db.once('open', () => {
  console.log('Connected to MongoDB');
});
app.use(bodyParser.json());
app.use(cors());
app.listen(PORT, () => {
  console.log(`Server is running on port ${PORT}`);
});
1.4.建立 models
建立一個 models 目錄並定義 Todo 模型。
// models/Todo.js
const mongoose = require('mongoose');
const todoSchema = new mongoose.Schema({
  text: { type: String, required: true },
  completed: { type: Boolean, default: false },
  createdAt: { type: Date, default: Date.now },
});
const Todo = mongoose.model('Todo', todoSchema);
module.exports = Todo;
1.5.建立路線
建立路由目錄並定義 CRUD 操作的路由
// routes/todoRoutes.js
const express = require('express');
const Todo = require('../models/Todo');
const router = express.Router();
// Create a new todo
router.post('/', async (req, res) => {
  try {
    const todo = new Todo({
      text: req.body.text,
    });
    await todo.save();
    res.status(201).json(todo);
  } catch (err) {
    res.status(400).json({ error: err.message });
  }
});
// Get all todos
router.get('/', async (req, res) => {
  try {
    const todos = await Todo.find();
    res.status(200).json(todos);
  } catch (err) {
    res.status(400).json({ error: err.message });
  }
});
// Update a todo
router.put('/:id', async (req, res) => {
  try {
    const todo = await Todo.findByIdAndUpdate(req.params.id, req.body, { new: true });
    res.status(200).json(todo);
  } catch (err) {
    res.status(400).json({ error: err.message });
  }
});
// Delete a todo
router.delete('/:id', async (req, res) => {
  try {
    await Todo.findByIdAndDelete(req.params.id);
    res.status(200).json({ message: 'Todo deleted successfully' });
  } catch (err) {
    res.status(400).json({ error: err.message });
  }
});
module.exports = router;
將路由整合到index.js 檔案中。
//index.js
const todoRoutes = require('./routes/todoRoutes');
app.use('/api/todos', todoRoutes);
2.1.初始化React專案
使用 Create React App 建立一個新的 React 專案。
npx create-react-app client
cd client
2.2.安裝依賴項
安裝 Axios 以發出 HTTP 請求。
npm install axios
2.3.建立元件
建立一個元件目錄並新增以下元件:TodoList、TodoItem 和 TodoForm。
待辦事項清單元件
// src/components/TodoList.js
import React, { useEffect, useState } from 'react';
import axios from 'axios';
import TodoItem from './TodoItem';
import TodoForm from './TodoForm';
const TodoList = () => {
  const [todos, setTodos] = useState([]);
  useEffect(() => {
    fetchTodos();
  }, []);
  const fetchTodos = async () => {
    const response = await axios.get('http://localhost:5000/api/todos');
    setTodos(response.data);
  };
  const addTodo = async (text) => {
    const response = await axios.post('http://localhost:5000/api/todos', { text });
    setTodos([...todos, response.data]);
  };
  const updateTodo = async (id, updatedTodo) => {
    const response = await axios.put(`http://localhost:5000/api/todos/${id}`, updatedTodo);
    setTodos(todos.map(todo => (todo._id === id ? response.data : todo)));
  };
  const deleteTodo = async (id) => {
    await axios.delete(`http://localhost:5000/api/todos/${id}`);
    setTodos(todos.filter(todo => todo._id !== id));
  };
  return (
    <div>
      <h1>Todo List</h1>
      <TodoForm addTodo={addTodo} />
      {todos.map(todo => (
        <TodoItem
          key={todo._id}
          todo={todo}
          updateTodo={updateTodo}
          deleteTodo={deleteTodo}
        />
      ))}
    </div>
  );
};
export default TodoList;
TodoItem 元件
// src/components/TodoItem.js
import React from 'react';
const TodoItem = ({ todo, updateTodo, deleteTodo }) => {
  const toggleComplete = () => {
    updateTodo(todo._id, { ...todo, completed: !todo.completed });
  };
  return (
    <div>
      <input
        type="checkbox"
        checked={todo.completed}
        onChange={toggleComplete}
      />
      <span style={{ textDecoration: todo.completed ? 'line-through' : 'none' }}>
        {todo.text}
      </span>
      <button onClick={() => deleteTodo(todo._id)}>Delete</button>
    </div>
  );
};
export default TodoItem;
TodoForm元件
// src/components/TodoForm.js
import React, { useState } from 'react';
const TodoForm = ({ addTodo }) => {
  const [text, setText] = useState('');
  const handleSubmit = (e) => {
    e.preventDefault();
    addTodo(text);
    setText('');
  };
  return (
    <form onSubmit={handleSubmit}>
      <input
        type="text"
        value={text}
        onChange={(e) => setText(e.target.value)}
        placeholder="Add a new todo"
      />
      <button type="submit">Add</button>
    </form>
  );
};
export default TodoForm;
2.4.在應用程式中整合元件
更新 App.js 檔案以包含 TodoList 元件。
// src/App.js
import React from 'react';
import TodoList from './components/TodoList';
function App() {
  return (
    <div className="App">
      <TodoList />
    </div>
  );
}
export default App;
2.5.執行應用程式
啟動後端伺服器和 React 前端。
node index.js
npm run