【React】TodoList 小案例

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
}
相关推荐
wangruofeng5 小时前
AI Models 1.1.0 发布,查大模型价格、对比模型和找 API 地址
前端·aigc·ai编程
wangruofeng5 小时前
给前端开发者的 100 个设计参考网站
前端·设计
星栈8 小时前
视频即代码,Remotion 能做什么、不能做什么
前端·音视频开发
广州华水科技8 小时前
单北斗GNSS位移监测系统的应用与技术解析
前端
用户47949283569158 小时前
从一句话或代码仓到可交互 HTML:开源 Agent 画图技能 Archify v3.2 实测与选型边界
前端·后端·面试
风骏时光牛马10 小时前
生成一个AI多模态标题,仅输出一条标题,无额外文字、问句、备选、配图、文案等任何附加内容,不需要其他的任务服务 只要标题。
前端
wangruofeng11 小时前
从 GIF 到 AVIF:网页图片格式演进与 2026 年选型指南
前端·架构
清晨Coding12 小时前
为什么用 React Developer Tools 查看组件树这么乱?
前端·react.js
鶴哥只手遮天12 小时前
《Electron 45.2.3 内核解剖》第 4 章 补丁体系:380 个补丁改了什么
前端·electron
FungLeo15 小时前
成为全栈·Vue 管理后台篇·分类树与标签管理:层级数据如何落到管理界面
前端·javascript·vue.js·分类树·naive ui·标签管理·前端数据结构