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 动手改
- 把 4.1 计数器加一个「清零」按钮(
setCount(0)) - 给 4.2 页面加「已提交名单」:用不可变更新
setNames([...names, name])记下每次提交 - 用不可变方式把数组第 3 项文字改成「学习中」
4.5 小结
useState创建记忆,set 触发重渲染- 事件名驼峰 onClick/onChange;受控输入 value+onChange 双剑合璧
- 不可变更新:数组展开、对象展开,禁止 push/直接改