3.6 事件处理与受控表单


3.6 事件处理与受控表单

本节摘要:React 用驼峰事件属性做声明式绑定,用户输入则通过"受控组件"桥接进 state。理解受控与非受控的取舍,再处理好几类常用输入(文本、勾选、单选、下拉),表单就顺手了。本节把事件与表单一起串完。

本节阅读目标
阅读完本节,你应当能够:

  1. 在 JSX 里正确绑定事件并访问事件对象。
  2. 讲清受控组件为什么是表单的默认选择。
  3. 用受控方式处理文本、勾选、单选与下拉几类输入。

一、事件绑定:声明式而非监听器

React 不要求你手动 addEventListener,而是用驼峰属性直接绑定:

function Button() { const handleClick = (e) => { e.preventDefault(); // 需要时阻止默认行为 console.log('点了', e.target); }; return <button onClick={handleClick}>点击</button>; }

事件对象 e 由 React 统一包装,用法与原生事件基本一致。命名上:onClick、onChange、onSubmit 等规则一目了然,这与 Vue 的 v-on 是两套风格的分界(第 4.6 节会对比)。

二、受控组件:输入即状态

受控组件的精髓:输入框的值绑定 state,值一变就回写 state,state 一变又刷新输入框。两路绑定由 state 做唯一数据源:

function LoginForm() { const [name, setName] = useState(''); const submit = (e) => { e.preventDefault(); console.log('提交', name); }; return ( <form onSubmit={submit}> <input value={name} onChange={(e) => setName(e.target.value)} /> <button type="submit">登录</button> </form> ); }

value 绑定 name,onChange 把输入写回 name,下值可即时格式化、校验,也方便受控——这正是"界面是数据的函数"在表单维度的写照。

三、几种常用输入的处理

输入类型 绑定方式 说明
文本输入框 value + onChange 用 target.value 回写
复选框 checked + onChange 注意用 checked 而非 value
单选组 checked 按值比对 同一组用同一 state 判中
下拉选择 value + onChange 用 select 的 value

处理一段综合示例:

function Settings() { const [pref, setPref] = useState({ theme: 'light', agree: false }); const set = (key) => (e) => setPref({ ...pref, [key]: key === 'agree' ? e.target.checked : e.target.value }); return ( <form> <select value={pref.theme} onChange={set('theme')}> <option value="light">亮色</option> <option value="dark">暗色</option> </select> <label> <input type="checkbox" checked={pref.agree} onChange={set('agree')} /> 同意协议 </label> </form> ); }

勾选用 checked、其余用 value,是这几类输入最常出错的一处分界。

四、非受控:什么时候能省

非受控组件让 DOM 自己存状态,靠 ref 在需要时取值,少一次两路绑定。适合:纯一次性取值、无需实时校验的表单。但一旦"输入要影响别的 UI"或"需要受控回写",就回到受控。默认先受控,非受控是例外。

顺带说一句受控流程带来的另一个好处:因为唯一数据源是 state,做"输入格式化"就只是"在 onChange 里改一下再写回",做"校验提示"也只是读 state 的派生值。这两件事在非受控写法里都得额外翻 DOM,繁琐得多。所以只要你的表单有任何联动、校验或格式化需求,受控都是更短的路——这不是偏好,而是"数据进 state"后自然衍生的便利。

五、图:受控 vs 非受控的数据路径

图:受控与非受控表单的数据流对照

图:受控与非受控表单的数据流对照

五·一、一个把"代码量"压下来的省心写法

多数受控表单字段的 onChange 长得一模一样:"收到事件 → 把 key 对应的值写回 state"。重复到第二个字段就值得抽一个小工具,让每个输入框都只声明"我是哪个 key":

function Account() { const [form, setForm] = useState({ email: '', age: '' }); const bind = (key) => (e) => setForm((f) => ({ ...f, [key]: e.target.value })); return ( <> <input value={form.email} onChange={bind('email')} placeholder="邮箱" /> <input value={form.age} onChange={bind('age')} placeholder="年龄" /> </> ); }

bind(key) 返回的每个事件处理器都知道自己该更新哪个字段,十几个字段也只是一排 bind('xxx'),可读性不掉、改动点集中。请看它怎么处理"更新依赖旧值"这一茬:回调里用的是 setForm((f) => ...) 的函数式更新,而不是直接读闭包里的 form——这一步避免了连续多次快速输入时读到过期值的问题。这个"传 key 的函数返回处理器"的套路,是受控表单里最有泛化价值的一种,你在 4.6 节 Vue 的双向绑定里会看到它被框架直接内建成了 v-model,原理正是同一条"key 属性写回 state"的缩写。

六、常见坑

  • form 提交刷新页面:没 preventDefault 时表单提交会整页刷新,忘加是高频翻车点。
  • 勾选用 value 而非 checked:导致"永远勾不上"或状态错乱。
  • 受控值来源不稳:value 绑定了一个会被重置的外部量,输入会"卡死"。

本节要点回顾

  • 事件绑定:驼峰属性 + 声明式,事件对象由 React 包装。
  • 受控组件:state 做唯一数据源,两路绑定,可校验可格式化。
  • 四类输入:文本用 value、勾选用 checked 为一处分界点。
  • 何时非受控:一次性取值、无实时交互时才用。

React 的内容在此收束。从环境搭建到事件表单,你已经能在 React 里写一个自作完整的小应用。下一章我们用同一套思想跨到 Vue——你会发现组件、单向数据流、响应式如此熟悉,只是语法换了一批。


作者与出处
原作者: 灏天文库
来源:灏天文库
整理: 灏天文库整理
由灏天文库平台收录,内容或由平台用户上传,仅供学习交流
发布者: 作者: 灏天文库 转发
评论区 (0)
U