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