用gpt-oss零改动实现一个todo应用

上次是文字型的,这次我们来写点代码试试,看看效果怎么样
首先肯定是比较简单的尝试开始
写一个todo应用,主要是看能不能零改动就跑起来
用最简单的提示词
1
帮我写一个todo应用,基于react

然后就给出了代码

下面给你一个 最小可运行 的 Todo 应用示例,基于 React + Vite(如果你习惯 create-react-app,思路几乎完全一样,只是文件夹结构略有差异)。
应用包含:

组件作用
TodoInput新增条目输入框
TodoItem单个待办项(支持完成/取消、删除)
TodoList渲染 Todo 列表
TodoApp/App主容器,维护全局 todos 状态

安装依赖

1
2
3
4
npm create vite@latest todo-app --template react
cd todo-app
npm i
npm run dev

下面按文件展示完整代码(把对应文件替换或新建即可)。


1️⃣ src/main.jsx

1
2
3
4
5
6
import React from "react";
import { createRoot } from "react-dom/client";
import App from "./App";
import "./index.css";

createRoot(document.getElementById("root")).render(<App />);

2️⃣ src/App.jsx

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
import React, { useState, useEffect } from "react";
import TodoInput from "./components/TodoInput";
import TodoList from "./components/TodoList";
import "./App.css";

/**
* 关键点:
* 1. 用 useState 存放 todos
* 2. 用 useEffect 做本地存储同步,仅在组件挂载时读取一次
* 3. 处理增删改的通用方法
*/
function App() {
const [todos, setTodos] = useState([]);

// 从 localStorage 初始化
useEffect(() => {
const stored = localStorage.getItem("todos");
if (stored) {
setTodos(JSON.parse(stored));
}
}, []);

// 每次 todos 变更时同步到 localStorage
useEffect(() => {
localStorage.setItem("todos", JSON.stringify(todos));
}, [todos]);

const addTodo = (title) => {
const newTodo = {
id: Date.now(),
title,
completed: false,
};
setTodos([newTodo, ...todos]);
};

const toggleComplete = (id) => {
setTodos((prev) =>
prev.map((t) =>
t.id === id ? { ...t, completed: !t.completed } : t
)
);
};

const deleteTodo = (id) => {
setTodos((prev) => prev.filter((t) => t.id !== id));
};

return (
<div className="app-container">
<h1>Todo List</h1>
<TodoInput addTodo={addTodo} />
<TodoList
todos={todos}
toggleComplete={toggleComplete}
deleteTodo={deleteTodo}
/>
</div>
);
}

export default App;

3️⃣ src/components/TodoInput.jsx

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
import React, { useState } from "react";
import "./TodoInput.css";

const TodoInput = ({ addTodo }) => {
const [value, setValue] = useState("");

const handleSubmit = (e) => {
e.preventDefault();
const trimmed = value.trim();
if (trimmed) {
addTodo(trimmed);
setValue("");
}
};

return (
<form className="todo-input" onSubmit={handleSubmit}>
<input
type="text"
placeholder="Add a new todo..."
value={value}
onChange={(e) => setValue(e.target.value)}
/>
<button type="submit">Add</button>
</form>
);
};

export default TodoInput;

4️⃣ src/components/TodoItem.jsx

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
import React from "react";
import "./TodoItem.css";

const TodoItem = ({ todo, toggleComplete, deleteTodo }) => {
return (
<div className="todo-item">
<label>
<input
type="checkbox"
checked={todo.completed}
onChange={() => toggleComplete(todo.id)}
/>
<span className={todo.completed ? "completed" : ""}>
{todo.title}
</span>
</label>
<button className="delete-btn" onClick={() => deleteTodo(todo.id)}>
×
</button>
</div>
);
};

export default TodoItem;

5️⃣ src/components/TodoList.jsx

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
import React from "react";
import TodoItem from "./TodoItem";
import "./TodoList.css";

const TodoList = ({ todos, toggleComplete, deleteTodo }) => {
if (todos.length === 0) {
return <p className="empty-msg">No todos yet. Add something!</p>;
}

return (
<div className="todo-list">
{todos.map((todo) => (
<TodoItem
key={todo.id}
todo={todo}
toggleComplete={toggleComplete}
deleteTodo={deleteTodo}
/>
))}
</div>
);
};

export default TodoList;

6️⃣ src/index.css(可选,简单全局样式)

1
2
3
4
5
6
7
8
9
10
11
12
/* 让页面充满屏幕并居中 */
html, body, #root {
height: 100%;
margin: 0;
font-family: system-ui, sans-serif;
}
body {
display: flex;
justify-content: center;
align-items: flex-start;
background: #f5f5f5;
}

7️⃣ src/App.css

1
2
3
4
5
6
7
8
9
10
11
12
13
14
.app-container {
width: 90%;
max-width: 480px;
background: #fff;
padding: 20px;
margin-top: 40px;
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
border-radius: 8px;
}

.app-container h1 {
text-align: center;
margin-bottom: 20px;
}

8️⃣ src/components/TodoInput.css

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
.todo-input {
display: flex;
margin-bottom: 20px;
}

.todo-input input {
flex: 1;
padding: 8px 12px;
font-size: 16px;
}

.todo-input button {
padding: 8px 12px;
margin-left: 8px;
font-size: 16px;
background: #4caf50;
color: #fff;
border: none;
cursor: pointer;
}

.todo-input button:hover {
background: #45a049;
}

9️⃣ src/components/TodoList.css

1
2
3
4
5
6
7
8
9
10
.todo-list {
display: flex;
flex-direction: column;
gap: 8px;
}

.empty-msg {
text-align: center;
color: #777;
}

🔟 src/components/TodoItem.css

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
.todo-item {
display: flex;
align-items: center;
justify-content: space-between;
padding: 8px 12px;
background: #fafafa;
border-radius: 4px;
}

.todo-item label {
flex: 1;
display: flex;
align-items: center;
}

.todo-item input[type="checkbox"] {
margin-right: 12px;
}

.todo-item .completed {
text-decoration: line-through;
color: #777;
}

.delete-btn {
background: transparent;
border: none;
color: #ff4444;
font-size: 20px;
cursor: pointer;
}

.delete-btn:hover {
color: #ff0000;
}

🎉 运行、打包

1
2
3
4
5
# 开发模式
npm run dev

# 生产构建
npm run build

🚀 小贴士

  • 本地存储:示例中使用 localStorage 让 Todo 在刷新后保留。生产环境可以接
    入后端或使用 IndexedDB 之类的更强大存储方案。
  • 状态管理:若项目日后变大,可考虑引入
    Redux/zustand/@tanstack/react-store 等。
  • 类型安全:使用 TypeScript 可以防止类型错误;把文件后缀改成 .tsx 并在
    package.json dependencies 增加 typescript@types/react
  • 单位测试jestreact-testing-library 能帮你保障组件逻辑正确。
  • 样式优化:考虑使用 styled-components/emotion/tailwindcss 等,让样
    式变得更易维护。

现在你就拥有一个完整、可直接跑起的 Todo React 应用,随时可以继续扩展功能(如过
滤、拖拽排序、用户登录等)。祝编码愉快 🚀!


在本地先建个应用

1
npm create vite@latest todo-app --template react

这里要注意下,要用vite的话要node版本在20.19+或者22.12+
还有是需要选择js版本,而不是ts
运行下

1
npm install

这里比较慢的话可以上镜像

1
npm config set registry https://registry.npmmirror.com

接着把生成的代码都保存成文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
├── src
│   ├── App.css
│   ├── App.jsx
│   ├── assets
│   │   └── react.svg
│   ├── components
│   │   ├── TodoInput.css
│   │   ├── TodoInput.jsx
│   │   ├── TodoItem.css
│   │   ├── TodoItem.jsx
│   │   ├── TodoList.css
│   │   └── TodoList.jsx
│   ├── index.css
│   └── main.jsx

运行命令

1
npm run dev

看到效果

结果是真的直接可用的,为什么要测这个呢,主要原因是一个模型本地可用的探讨,我认为如果是要本地可用,至少在这些相对简单的任务上能够完成得比较好,前面那个只是个提问,并没有什么可以验证正确性的,内容还是比较多的,对于做开发的来说能辅助编程还是比较重要的,并且能够完成一些相对独立完整的任务才是比较可用的初级门槛,只是对内存压力还是比较大的