1、React 中的 状态(State)与事件处理

4 阅读3分钟

太好了!我们继续深入 React 的核心概念 👇


这是从“静态组件”迈向“动态交互”的关键一步。掌握它,你的 React 应用才能真正“活起来”。


一、为什么需要 State?💡

在上一节中,我们学会了用 Props 接收外部数据。但 Props 是只读的,无法改变。

而现实中的 UI 是动态的:

  • 用户点击按钮 → 显示/隐藏内容
  • 输入表单 → 实时预览
  • 计数器 → 数字递增

👉 这些都需要组件“记住一些信息”,并在未来发生变化 —— 这就是 State(状态)

✅ 简单说:

  • Props:从父组件“传进来”的数据(外部输入)
  • State:组件自己“管理的数据”,会随时间变化

二、使用 useState Hook 管理状态 🪝

现代 React 使用 Hooks 来管理状态。最基础的就是 useState

✅ 基本语法:

import { useState } from 'react';

function Component() {
  const [state, setState] = useState(初始值);
  //       ↑         ↑
  //     当前状态   更新函数
}

🧪 示例 1:计数器

import { useState } from 'react';

function Counter() {
  const [count, setCount] = useState(0); // 初始值为 0

  return (
    <div>
      <p>你点击了 {count} 次</p>
      <button onClick={() => setCount(count + 1)}>
        +1
      </button>
      <button onClick={() => setCount(prev => prev - 1)}>
        -1
      </button>
    </div>
  );
}

⚠️ 注意:

  • 不要直接修改 count(如 count++),必须通过 setCount() 更新
  • 使用函数式更新 setCount(prev => prev + 1) 可避免闭包问题

🧪 示例 2:开关按钮(布尔状态)

function Toggle() {
  const [isOn, setIsOn] = useState(false);

  return (
    <button 
      onClick={() => setIsOn(!isOn)}
      style={{ background: isOn ? 'green' : 'gray' }}
    >
      {isOn ? '开' : '关'}
    </button>
  );
}

🧪 示例 3:文本输入框(受控组件)

function NameInput() {
  const [name, setName] = useState('');

  return (
    <div>
      <input
        type="text"
        value={name}           // 值由 state 控制
        onChange={(e) => setName(e.target.value)}  // 输入时更新 state
        placeholder="请输入名字"
      />
      <p>你好,{name || '陌生人'}!</p>
    </div>
  );
}

🔑 关键点:这个 <input> 被称为 受控组件(Controlled Component)
它的值完全由 React 状态控制,而不是浏览器自己维护。


三、事件处理详解 ⚡

React 中的事件处理和原生 DOM 类似,但有一些命名差异和注意事项。

✅ 基本写法

<button onClick={handleClick}>点我</button>
// 或内联
<button onClick={() => alert('hello')}>点我</button>

支持的常见事件:

事件类型React 写法
点击onClick
输入变化onChange
表单提交onSubmit
鼠标移入onMouseEnter
键盘按下onKeyDown

🛠️ 事件对象 e

和原生 JS 一样,事件回调接收一个 合成事件对象(SyntheticEvent)

function handleInputChange(e) {
  console.log(e.target.value); // 获取输入框的值
}

<input onChange={handleInputChange} />

💡 React 使用的是“合成事件(SyntheticEvent)”,它是跨浏览器兼容的包装器。


❗ 常见错误与避坑指南

错误写法正确做法说明
<button onClick={handleClick()}><button onClick={handleClick}>加括号会立即执行函数
直接修改 state 变量必须用 setState(...)否则不会触发重新渲染
在事件中忘记阻止默认行为e.preventDefault()如表单提交刷新页面

✅ 正确示例:表单提交

function LoginForm() {
  const [email, setEmail] = useState('');

  const handleSubmit = (e) => {
    e.preventDefault(); // 阻止页面刷新
    alert(`登录邮箱:${email}`);
  };

  return (
    <form onSubmit={handleSubmit}>
      <input
        type="email"
        value={email}
        onChange={(e) => setEmail(e.target.value)}
        required
      />
      <button type="submit">登录</button>
    </form>
  );
}

四、State 的原则与最佳实践 ✅

1. 单一职责原则

每个 state 变量尽量只管理一种数据

// ❌ 不推荐
const [userState, setUserState] = useState({ count: 0, name: '', visible: true });

// ✅ 推荐
const [count, setCount] = useState(0);
const [name, setName] = useState('');
const [visible, setVisible] = useState(true);

2. 使用函数式更新(Function Updates)

当新 state 依赖于旧 state 时,使用函数形式

setCount(prev => prev + 1); // 更安全,避免闭包陷阱

3. 不要在条件或循环中调用 Hook

✅ 必须在组件顶层使用 useState

// ❌ 错误
if (show) {
  const [name, setName] = useState('');
}

// ✅ 正确
const [name, setName] = useState('');

这是 React Hooks 的 规则之一:只能在顶层调用 Hook。


五、实战练习 🏋️‍♀️

来动手练一练吧!完成以下小任务:

✅ 练习 1:简易投票应用

  • 显示两个选项:“苹果” 和 “香蕉”
  • 每个都有一个票数和“+1”按钮
  • 点击后对应票数增加
// 提示结构
const [appleVotes, setAppleVotes] = useState(0);
const [bananaVotes, setBananaVotes] = useState(0);

✅ 练习 2:待办事项输入框

  • 一个输入框 + “添加”按钮
  • 输入内容后点击添加,清空输入框
  • (暂不显示列表,只需实现添加逻辑)
const [text, setText] = useState('');
const handleAdd = () => {
  if (text.trim()) {
    alert(`添加了:${text}`);
    setText(''); // 清空
  }
};

✅ 练习 3:切换主题按钮

  • 有一个按钮,点击切换“白天 / 夜间”模式
  • 背景颜色随之变化(浅灰 / 深灰)
  • 使用 classNamestyle 控制样式
const [isDark, setIsDark] = useState(false);
return (
  <div style={{ background: isDark ? '#333' : '#f0f0f0', padding: 20 }}>
    <button onClick={() => setIsDark(!isDark)}>
      {isDark ? '白天模式' : '夜间模式'}
    </button>
  </div>
);

✅ 总结:State 与事件核心要点

概念要点
useState[value, setValue] = useState(init)
更新状态必须通过 setValue(...),不能直接改变量
受控组件表单元素的值由 state 控制 + onChange 更新
事件处理使用驼峰命名,传函数引用,非调用
合成事件e.target, e.preventDefault()
函数式更新setValue(prev => prev + 1) 更安全
规则Hook 必须在顶层调用

🎯 下一步预告:
你已经能让组件“动起来”了!接下来我们要学习:
➡️ 组件之间如何通信?

  • 父传子(Props)
  • 子传父(回调函数)
  • 跨层级传递(Context)

是否继续?我可以带你进入 第三课:组件通信与数据流