🐸 青蛙编程网
首页 / 前端 / React / 4. state 与事件处理

4. state 与事件处理

这是什么:state = 组件的「记忆」,用 useState 创建,改动它就能让页面重新渲染。

为什么用:上一章的组件每次渲染都是全新的,普通变量改了不起作用——值用完就没了,组件根本记不住东西,页面也不会跟着变。

如何用:本章用 useState 写一个点击加一的计数器,再把点击、输入、提交这些事件用 onClick、onChange、onSubmit 接上,最后背下「不可变更新」铁律。

4.1 useState:给组件装记忆

计数器整段可跑:

<!DOCTYPE html>
<html lang='zh-CN'>
<head>
  <meta charset='utf-8'>
  <script src='https://registry.npmmirror.com/react/18.3.1/files/umd/react.production.min.js'></script>
  <script src='https://registry.npmmirror.com/react-dom/18.3.1/files/umd/react-dom.production.min.js'></script>
  <script src='https://registry.npmmirror.com/@babel/standalone/7.29.11/files/babel.min.js'></script>
</head>
<body>
  <div id='root'></div>

  <script type='text/babel' data-presets='react'>
    function Counter() {
      const [count, setCount] = React.useState(0)   // [当前值, 更新函数]

      return (
        <button onClick={() => setCount(count + 1)}>
          点了 {count} 次
        </button>
      )
    }

    ReactDOM.createRoot(document.getElementById('root')).render(<Counter />)
  </script>
</body>
</html>

你会看到:一颗「点了 0 次」的按钮,每点一下数字加一。

规则:

  • const [n, setN] = React.useState(初值)——解构写法
  • 更新必须用 set 函数(count++ 无效!)
  • set 之后 React 会用新数据重新跑一遍组件函数

4.2 事件绑定:点击、输入、提交一页打通

事件三件套(表单提交 + 受控输入 + 键盘)整段可跑:

<!DOCTYPE html>
<html lang='zh-CN'>
<head>
  <meta charset='utf-8'>
  <script src='https://registry.npmmirror.com/react/18.3.1/files/umd/react.production.min.js'></script>
  <script src='https://registry.npmmirror.com/react-dom/18.3.1/files/umd/react-dom.production.min.js'></script>
  <script src='https://registry.npmmirror.com/@babel/standalone/7.29.11/files/babel.min.js'></script>
</head>
<body>
  <div id='root'></div>

  <script type='text/babel' data-presets='react'>
    function NameBox() {
      const [name, setName] = React.useState('')

      function submit(e) {
        e.preventDefault()      // React 没有 .prevent 修饰符,手工调
        console.log('提交了', name)
      }

      return (
        <form onSubmit={submit}>
          <input
            value={name}                                   // 受控:值来自 state
            onChange={(e) => setName(e.target.value)}      // 输入回写 state
            onKeyDown={(e) => e.key === 'Enter' && submit(e)}   // 对应 Vue 的 .enter
          />
          <button type='submit'>提交</button>
          <p>你好,{name || '陌生人'}!</p>       {/* 输入到哪,页面跟到哪 */}
        </form>
      )
    }

    ReactDOM.createRoot(document.getElementById('root')).render(<NameBox />)
  </script>
</body>
</html>

你会看到:输入框里敲什么,下面一行就实时显示什么(受控输入);敲回车或点「提交」,运行场日志区出现「提交了 ×××」。

事件 React 写法
点击 onClick={fn}
输入 onChange={fn}
键盘 onKeyDown / onKeyUp
提交 onSubmit={fn}
鼠标 onMouseEnter 等

这就是 Vue v-model 的 React 手工版:value + onChange 成对出现,两个字段永远同步。

4.3 不可变更新(重要!)

state(尤其数组/对象)要创建新的,不能原地改,四个高频操作的正确姿势一张表背下来:

操作 ❌ 反面教材(React 检测不到变化) ✅ 创建新数据再 set
新增 tasks.push(newTask) setTasks([...tasks, newTask])
改属性 user.age = 12 setUser({ ...user, age: 12 })
删除 tasks.splice(i, 1) setTasks(tasks.filter((t) => t.id !== id))
更新一项 task.done = true setTasks(tasks.map((t) => t.id === id ? { ...t, done: !t.done } : t))

强制记忆:数组:[...];对象:{...}。Vue 里直接改属性就生效,React 必须「旧换新」——两边最常见的概念差就在这。

4.4 动手改

  1. 把 4.1 计数器加一个「清零」按钮(setCount(0))
  2. 给 4.2 页面加「已提交名单」:用不可变更新 setNames([...names, name]) 记下每次提交
  3. 用不可变方式把数组第 3 项文字改成「学习中」

4.5 小结

  • useState 创建记忆,set 触发重渲染
  • 事件名驼峰 onClick/onChange;受控输入 value+onChange 双剑合璧
  • 不可变更新:数组展开、对象展开,禁止 push/直接改

下一章:Hooks:useState 与 useEffect 🐸

📥 下载本章代码

ZIP 内含本章全部代码文件与说明,解压即用,与教程内容同步生成。