面向 Vue3 开发者的 React 快速上手系列
Vue 的 v-model 是双向绑定的神器,一个指令搞定表单。React 没有 v-model------它推崇受控组件(Controlled Component):状态是唯一数据源,输入框的值由 state 决定,用户输入时通过事件更新 state。这一篇讲透事件和表单。
一、事件处理:从 @click 到 onClick
vue
<!-- Vue3 -->
<button @click="handleClick">点我</button>
<button @click="count++">+1</button>
<input @input="onInput" />
jsx
// React:驼峰命名 + 传函数引用
<button onClick={handleClick}>点我</button>
<button onClick={() => setCount(count + 1)}>+1</button>
<input onChange={onInput} />
要点:
- 事件名驼峰 :
onClick、onChange、onInput、onSubmit、onMouseEnter...... - 传的是函数引用 (
onClick={handleClick}),不是函数调用(onClick={handleClick()}❌ 会立即执行); - 需要传参时用箭头函数包一层:
onClick={() => handleDelete(id)}。
事件对象
jsx
function Demo() {
const handleClick = (e) => {
e.preventDefault() // 阻止默认行为,对标 Vue 的 .prevent
e.stopPropagation() // 阻止冒泡,对标 .stop
console.log(e.target)
}
return <a href="#" onClick={handleClick}>链接</a>
}
事件修饰符去哪了?
Vue 有 .prevent、.stop、.once、.enter 等修饰符。React 没有,全部手写:
| Vue3 | React |
|---|---|
@click.prevent |
在处理函数里 e.preventDefault() |
@click.stop |
在处理函数里 e.stopPropagation() |
@keyup.enter="fn" |
onKeyUp={e => e.key === 'Enter' && fn()} |
@click.once |
自己用状态标志控制 |
二、受控组件:理解 React 的表单哲学
Vue 的 v-model 本质是"值绑定 + 事件监听"的语法糖。React 让你显式地写出这两半:
vue
<!-- Vue3 -->
<input v-model="name" />
jsx
// React:这就是 v-model 的"拆解版"
<input
value={name} // 值来自 state
onChange={e => setName(e.target.value)} // 输入时更新 state
/>
这就叫受控组件 :输入框显示什么,完全由 state name 决定;用户敲键盘触发 onChange,我们更新 state,state 变了组件重渲染,输入框显示新值。数据流是单向闭环的------这正是 React 的核心理念。
用户输入 → onChange → setState → 重渲染 → value 更新 → 显示新值
一开始会觉得比 v-model 啰嗦,但好处是:任何时候 state 就是表单的唯一真相,校验、格式化、联动都极其直观。
三、各种表单元素
文本框 / 文本域
jsx
<input value={text} onChange={e => setText(e.target.value)} />
<textarea value={desc} onChange={e => setDesc(e.target.value)} />
复选框(用 checked 而非 value)
jsx
<input
type="checkbox"
checked={agreed}
onChange={e => setAgreed(e.target.checked)}
/>
单选框
jsx
<label>
<input type="radio" value="male" checked={gender === 'male'}
onChange={e => setGender(e.target.value)} /> 男
</label>
<label>
<input type="radio" value="female" checked={gender === 'female'}
onChange={e => setGender(e.target.value)} /> 女
</label>
下拉选择(注意和 Vue 不同)
Vue 在 <option> 上写 selected,React 则把 value 放在 <select> 上:
jsx
<select value={city} onChange={e => setCity(e.target.value)}>
<option value="bj">北京</option>
<option value="sh">上海</option>
</select>
四、多字段表单:一个 handler 搞定
字段多时,别写一堆 setter。用一个对象 state + 通用 handler(靠 name 属性区分):
jsx
import { useState } from 'react'
function SignupForm() {
const [form, setForm] = useState({ username: '', email: '', password: '' })
// 通用 handler:根据 input 的 name 更新对应字段
const handleChange = (e) => {
const { name, value } = e.target
setForm(prev => ({ ...prev, [name]: value })) // 计算属性名 + 不可变更新
}
const handleSubmit = (e) => {
e.preventDefault() // 阻止表单默认刷新页面
console.log('提交', form)
}
return (
<form onSubmit={handleSubmit}>
<input name="username" value={form.username} onChange={handleChange} placeholder="用户名" />
<input name="email" value={form.email} onChange={handleChange} placeholder="邮箱" />
<input name="password" type="password" value={form.password} onChange={handleChange} placeholder="密码" />
<button type="submit">注册</button>
</form>
)
}
注意 [name]: value 这个计算属性名语法------它让一个 handler 能更新任意字段,是 React 表单的常用技巧。
五、表单提交
用 <form onSubmit> 并 e.preventDefault()(阻止浏览器默认刷新),比给按钮绑 click 更规范(支持回车提交):
jsx
<form onSubmit={handleSubmit}>
{/* ... */}
<button type="submit">提交</button>
</form>
六、非受控组件与 useRef(偶尔用)
如果不想为每个输入维护 state(比如简单表单、或对接第三方 DOM 库),可以用非受控组件 ,通过 ref 在提交时直接读 DOM 值。这类似 Vue 里用模板 ref 拿 DOM:
jsx
import { useRef } from 'react'
function SearchBox() {
const inputRef = useRef(null)
const handleSearch = () => {
console.log(inputRef.current.value) // 直接读 DOM 的值
}
return (
<>
<input ref={inputRef} defaultValue="" /> {/* 用 defaultValue 而非 value */}
<button onClick={handleSearch}>搜索</button>
</>
)
}
受控 vs 非受控:默认优先用受控组件(state 驱动,可控性强);只有在性能敏感或对接命令式 DOM 库时才用非受控。
七、React 19 表单新特性(了解)
React 19 引入了 useActionState、<form action={fn}>、useFormStatus 等,能进一步简化表单提交与 pending 状态处理,减少手写 useState。等你熟悉了受控组件基础后,可以去了解这些新 API------它们让表单代码更接近"声明式"。
八、对照速查
| 场景 | Vue3 | React |
|---|---|---|
| 点击事件 | @click="fn" |
onClick={fn} |
| 传参 | @click="fn(id)" |
onClick={() => fn(id)} |
| 阻止默认 | @click.prevent |
e.preventDefault() |
| 阻止冒泡 | @click.stop |
e.stopPropagation() |
| 回车触发 | @keyup.enter |
onKeyUp={e => e.key==='Enter' && fn()} |
| 文本双向绑定 | v-model |
value + onChange(受控) |
| 复选框 | v-model |
checked + onChange |
| 下拉 | v-model on select |
value on <select> |
| 直接读 DOM 值 | 模板 ref |
useRef + .current.value |
小结
- 事件用驼峰 + 函数引用;传参包箭头函数;修饰符全部手写。
- React 没有
v-model,用受控组件 :value+onChange手动闭环,state 是唯一真相。 - 复选框用
checked,下拉的value放在<select>上。 - 多字段表单用一个对象 state +
[name]: value通用 handler。 - 简单场景可用
useRef做非受控组件,但默认优先受控。
下一篇讲状态管理 useState 与 useReducer