一网打尽react手写题(上)

1. Counter 计数器

推导链

Q1: 需求是什么?

→ 加、减、清空 + input 联动 + 不能为负数

Q2: 核心是什么?

→ 受控组件:value 绑定 state,onChange 更新 state

Q3: 边界怎么处理?

Math.max(0, val) 防负数

|| 0 防 NaN

javascript 复制代码
import { useState } from "react";


export default function Counter() {
    const [count, setCount] = useState(0)
    const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
        const value = parseInt(e.target.value) || 0
        setCount(Math.max(value, 0))
    }

    return (
        <div className="max-w-md mx-auto p-6 flex items-center gap-2">
            {/* flex + gap:横向排列 + 间距 */}

            <input
                type="number"
                value={count}
                onChange={handleChange}
                className="w-20 px-2 py-1 border rounded text-center"
            />
            {/* w-20:固定宽度,text-center:数字居中 */}
            <button
                onClick={() => setCount(c => c + 1)}
                className="px-3 py-1 bg-blue-500 text-white rounded hover:bg-blue-600 active:scale-95"

            >
                +
            </button>
            {/* active:scale-95:点击时缩小,有反馈感 */}
            <button
                onClick={() => setCount(c => Math.max(c - 1, 0))}
                className="px-3 py-1 bg-gray-200 rounded hover:bg-gray-300 active:scale-95"
            >
                -
            </button>

            <button
                onClick={() => setCount(0)}
                className="px-3 py-1 text-red-500 hover:text-red-600"
             
            >
                重置
            </button>
               {/* 文字按钮,不需要背景 */}
        </div>
    )
}

2. TodoList

推导链

Q1: 需求?

→ 增删改查 + 完成状态切换

Q2: 数据结构?

{ id, text, completed }[]

Q3: 核心操作?

→ 增:push 新项

→ 删:filter 过滤

→ 改:map 找到对应 id 修改

→ 切换:map 翻转 completed

javascript 复制代码
import { useState } from 'react'

interface Todo {
  id: number
  text: string
  completed: boolean
}

export default function TodoList() {
  const [todos, setTodos] = useState<Todo[]>([])
  const [input, setInput] = useState('')

  const addTodo = () => {
    if (!input.trim()) return
    setTodos([...todos, { id: Date.now(), text: input, completed: false }])
    setInput('')
  }

  const deleteTodo = (id: number) => {
    setTodos(todos.filter(t => t.id !== id))
  }

  const toggleTodo = (id: number) => {
    setTodos(todos.map(t => t.id === id ? { ...t, completed: !t.completed } : t))
  }

  return (
    <div>
      <input value={input} onChange={e => setInput(e.target.value)} />
      <button onClick={addTodo}>添加</button>
      <ul>
        {todos.map(todo => (
          <li key={todo.id}>
            <span 
              style={{ textDecoration: todo.completed ? 'line-through' : 'none' }}
              onClick={() => toggleTodo(todo.id)}
            >
              {todo.text}
            </span>
            <button onClick={() => deleteTodo(todo.id)}>删除</button>
          </li>
        ))}
      </ul>
    </div>
  )
}

3. CountDown 倒计时

推导链

Q1: 需求?

→ 显示剩余时间,每秒更新,到 0 停止

Q2: 核心?

→ useEffect + setInterval

→ 清理:return clearInterval

Q3: 注意点?

→ 依赖数组要包含 count,或用函数式更新

→ count <= 0 时 clearInterval

javascript 复制代码
import { useState, useEffect } from 'react'

export default function CountDown({ initial = 10 }: { initial?: number }) {
    const [count, setCount] = useState(initial)
    useEffect(() => {
        if (count <= 0) return
        const timer = setTimeout(() => setCount(c => c - 1), 1000)
        return () => clearTimeout(timer)
    }, [count])

    const reset = () => setCount(initial)

    return (
        <div className='min-h-screen flex items-center justify-center'>
            <div className='flex items-center gap-2 p-6'>
                <span className='w-20 px-2 py-1 border rounded-e text-center'>{count}</span>
                <button className='px-3 py-1 bg-blue-500 rounded hover:bg-blue-600 active:scale-95'
                    onClick={reset}
                >reset</button>
            </div>
        </div>
    )
}

这三个例子(Counter、TodoList、CountDown)它们从简单到复杂,完整覆盖了 React 开发中最核心的几个概念。通过分析代码,我们可以总结出以下 React 关键知识点:

🧠 核心状态管理 (State Management)

这是 React 的基石,主要体现在 useState 的使用上。

  • 状态声明与初始化:
    • 使用 useState 定义组件内部的状态。
    • 类型安全: 在 TypeScript 环境下,明确指定了状态类型,如 useState<Todo[]>([])useState(0)
  • 状态更新模式:
    • 直接更新: 当新状态不依赖于旧状态时(如重置 setCount(0) 或更新输入框 setInput),直接传入新值。
    • 函数式更新: 当新状态依赖于旧状态时(如 setCount(c => c + 1) 或倒计时递减),使用回调函数形式。这确保了在异步更新或批量更新场景下,获取到的是最新的 state 值。
  • 不可变性原则:
    • 在 TodoList 中,没有直接修改数组(如 pushsplice),而是使用扩展运算符 [...todos, newItem]filtermap 来创建新的数组引用。这是 React 检测状态变化并触发重渲染的关键。

🔄 副作用处理 (Side Effects)

主要体现在 CountDown 组件中,用于处理时间、订阅等"副作用"。

  • useEffect 的使用:
    • 用于在组件渲染后执行副作用逻辑(这里是启动定时器)。
  • 依赖数组:
    • [count]:表示当 count 变化时,重新执行 Effect。
  • 清理机制:
    • return () => clearTimeout(timer):这是防止内存泄漏的关键。当组件卸载或 count 变化导致 Effect 重跑时,先清理上一次的定时器。
  • 防御性编程:
    • if (count <= 0) return:在 Effect 内部做逻辑判断,避免不必要的定时器设置。

🎛️ 表单与受控组件 (Forms & Controlled Components)

主要体现在 CounterTodoList 的输入框中。

  • 受控组件模式:
    • <input value={state} onChange={...} />:Input 的值完全由 React 的 state 驱动,而不是 DOM 自身的值。
  • 事件处理:
    • 处理 onChange 事件,从事件对象 e.target.value 中提取值。
    • 类型断言: 在 TS 中使用 React.ChangeEvent<HTMLInputElement> 确保类型安全。
  • 数据清洗:
    • handleChange 中使用 parseInt|| 0 处理空值或非法字符,防止 NaN 导致程序崩溃。

📐 列表渲染与键 (Lists & Keys)

主要体现在 TodoList 组件中。

  • map 渲染:
    • 使用 JavaScript 的 array.map() 方法将数据数组转换为 JSX 元素数组。
  • key 属性:
    • <li key={todo.id}>:为列表项提供唯一的标识符。这有助于 React 的 Diff 算法高效地更新 DOM,避免渲染错误。代码中使用 Date.now() 生成唯一 ID 是一种常见的简单策略。

⚛️ 事件处理与交互逻辑

  • 事件绑定: onClick 等标准 React 事件。
  • 内联处理 vs 定义函数:
    • 简单逻辑直接在 JSX 中写箭头函数 onClick={() => setCount(...)}
    • 复杂逻辑(如添加 Todo)提取为独立函数 addTodo
  • 逻辑边界处理:
    • 防负数: Math.max(0, val)
    • 空值处理: if (!input.trim()) return 防止添加空任务。

📌 总结表

表格

知识点 涉及组件 核心代码/概念
useState 全部 const [count, setCount] = useState(0)
函数式更新 Counter, CountDown setCount(c => c + 1)
不可变数据 TodoList [...todos, newItem], filter, map
useEffect CountDown 定时器设置与 clearTimeout 清理
受控组件 Counter, TodoList value={state} + onChange
列表渲染 TodoList todos.map(...) + key
类型安全 全部 useState<Todo[]>, React.ChangeEvent
相关推荐
2601_955354462 小时前
SEO新手如何快速入门学习
前端·学习·搜索引擎
小和尚敲木头2 小时前
router.push(‘/‘)跳转不触发重定向
开发语言·前端·javascript
misty youth2 小时前
提示词合集【自用】
开发语言·前端·ai编程
zzginfo2 小时前
ES6 中的 “?.” 可选链运算符用法
前端·ecmascript·es6
战族狼魂2 小时前
Claude Code 源码泄露事件
前端·npm·node.js
We་ct2 小时前
LeetCode 67. 二进制求和:详细题解+代码拆解
前端·数据结构·算法·leetcode·typescript
还是大剑师兰特2 小时前
为什么要用 import.meta.glob 加载 SVG 图标库
开发语言·前端·javascript
渣渣xiong2 小时前
《从零开始:前端转型AI agent直到就业第三天》
前端·ai编程
天若有情6732 小时前
从C++ RefInt到JS Object.defineProperty:吃透响应式监听的本质(学生视角)
开发语言·javascript·c++