page.tsx
tsx
"use client"
import AddTodo from "@/components/AddTodo";
import TodoList from "@/components/TodoList";
import TodoFilter from "@/components/TodoFilter";
import {useState} from "react";
import {Todo} from "@/types";
export default function Home() {
// todos 为事项对象的数组 { id: number, text:string, completed:boolean }
const [todos, setTodos] = useState<Todo[]>([])
const [filter, setFilter] = useState('all')
const addTodo = (text: string) => {
const newTodo = {
id: Date.now(),
text,
completed: false
}
setTodos([...todos, newTodo])
}
const deleteTodo = (id:number) => {
setTodos(todos.filter(todo=> todo.id !== id))
}
const toggleTodo = (id: number) => {
setTodos(todos.map(todo=> {
if (todo.id === id) {
todo.completed = !todo.completed
}
return todo
}))
}
const getFilteredTodos = () => {
switch (filter) {
case 'completed':
return todos.filter(todo => todo.completed)
case 'active':
return todos.filter(todo => !todo.completed)
default:
return todos
}
}
return (
<div>
<h1>TodoList</h1>
<AddTodo addTodo={addTodo}/>
<TodoList todos={getFilteredTodos()} deleteTodo={deleteTodo} toggleTodo={toggleTodo} />
<TodoFilter setFilter={setFilter}/>
</div>
)
}
AddTodo.tsx
tsx
import React, {useState} from "react";
interface AddTodoProps {
addTodo: (text: string) => void
}
function AddTodo({addTodo}: AddTodoProps) {
const [text, setText] = useState('')
const handleSubmit = (e: React.FormEvent<HTMLFormElement>) => {
e.preventDefault()
if (text.trim() === '') {
return
}
addTodo(text)
setText('')
}
return (
<form onSubmit={handleSubmit}>
<input type="text" value={text} onChange={(e) => setText(e.target.value)}/>
<button>新建事项</button>
</form>
)
}
export default AddTodo
TodoFilter.tsx
tsx
interface SetFilterProps {
setFilter: (text: string) => void
}
function TodoFilter ({ setFilter }: SetFilterProps){
return (
<div>
<button onClick={()=>setFilter('all')}>全部</button>
<button onClick={()=>setFilter('active')}>待办</button>
<button onClick={()=>setFilter('completed')}>已办</button>
</div>
)
}
export default TodoFilter
TodoItem.tsx
tsx
import {Todo} from "@/types";
interface TodoListProps {
todo: Todo
toggleTodo: (id: number) => void
deleteTodo: (id: number) => void
}
function TodoItem({todo, toggleTodo, deleteTodo}: TodoListProps) {
return (
<li style={{textDecoration: todo.completed ? 'line-through': 'none'}}>
{todo.text}
<button onClick={()=> toggleTodo(todo.id)}>切换</button>
<button onClick={()=> deleteTodo(todo.id)}>删除</button>
</li>
)
}
export default TodoItem
TodoList.tsx
tsx
import {Todo} from "@/types";
import TodoItem from "@/components/TodoItem";
interface TodoListProps {
todos: Array<Todo>
toggleTodo: (id: number) => void
deleteTodo: (id: number) => void
}
function TodoList({todos, toggleTodo, deleteTodo}: TodoListProps) {
return (
<ul>
{todos.map(todo => (
<TodoItem key={todo.id} todo={todo} toggleTodo={toggleTodo} deleteTodo={deleteTodo}/>
))}
</ul>
)
}
export default TodoList
types.ts
ts
export interface Todo {
id: number
text: string
completed: boolean
}
