news 2026/9/11 17:55:33

React 表单面试中受控组件与非受控组件怎么选?

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
React 表单面试中受控组件与非受控组件怎么选?

React 表单面试中受控组件与非受控组件怎么选?

【免费下载链接】front-end-interview-handbookFront End interview preparation materials for busy engineers (updated for 2026)项目地址: https://gitcode.com/GitHub_Trending/fr/front-end-interview-handbook

在 front-end-interview-handbook 的 React 面试手册中,表单是反复出现的一类编码题(Contact Form、Auth Code Input 等都涉及)。动手写表单之前,第一个决策就是输入值放在哪里:React state(受控组件)还是原生 DOM(非受控组件)。这个选择决定了后续写onChange还是写ref,也决定了面试官追问"实时校验""表单重置"时你能多快兑现。下面的实现和判断依据全部来自手册中 Forms in React Interviews 与 Signup Form Example 两篇文档。

受控与非受控:两种写法的完整实现

受控组件:值存在 React state 中

受控组件中,React 管理表单元素的状态:输入值存储在 state 变量中,通过onChange处理程序更新,值始终由 React 控制。

import { useState } from 'react'; function ControlledForm() { const [name, setName] = useState(''); function handleChange(event) { setName(event.target.value); } function handleSubmit(event) { event.preventDefault(); alert(`Submitted Name: ${name}`); } return ( <form onSubmit={handleSubmit}> <label> Name: <input type="text" value={name} onChange={handleChange} /> </label> <button type="submit">Submit</button> </form> ); }

用户每敲一个字符,onChange就用setName(event.target.value)更新 state;提交时直接从 state 读取当前值。

非受控组件:值留在 DOM 中

非受控组件中,输入值不由 React state 管理,而是由 DOM 本身持有。提交时通过两种方式取值:

  1. FormData:从event.target拿到表单元素,formData.get('name')的值对应<input>上的name属性;
  2. refs:用useRef()直接引用<input>,通过nameRef.current.value读取。
import { useRef } from 'react'; function UncontrolledForm() { const nameRef = useRef(); function handleSubmit(event) { event.preventDefault(); // Access form values using `FormData` const formData = new FormData(event.target); console.log('Name:', formData.get('name')); // Alternatively, access the <input> via a ref console.log('Name:', nameRef.current.value); } return ( <form onSubmit={handleSubmit}> <label> Name: <input type="text" name="name" ref={nameRef} /> </label> <button type="submit">Submit</button> </form> ); }

选择依据:对照表与面试中的默认倾向

手册给出的两种表单特性对照:

特性受控表单非受控表单
状态存储位置React 状态(useState原生 DOM
性能更新时需要重新渲染,大型表单可能出现问题简单用例下性能更好
校验易于实现需要手动校验
表单重置容易(setState("")需要ref.current.value = ""或触发'reset'事件
用例动态表单、校验、实时更新简单表单、文件上传、集成非 React 代码

对应的选择规则:

  • 需要动态校验、操作或跟踪用户输入(例如根据先前回答切换某些字段的可见性),或涉及嵌套表单状态时,用受控组件
  • 处理大型表单、与非 React 代码集成、或以性能为优先时,用非受控组件

手册对面试的明确建议:面试中的表单通常字段不多,两种方式都可行,但作者倾向默认选受控组件,除非表单极其简单。原因是面试官经常追加 follow-up,比如实时校验(live validation)、条件字段(conditional fields)、程序化重置(programmatic reset)——这些用受控 state 实现很直接,用 refs 和FormData则要额外搭管道。

受控写法的细节:checkbox 必须绑checked

文本类输入用value+event.target.value,而复选框是布尔值,这是文档标注的"常见面试失误点":checkbox 绑checked而非value,读取event.target.checked而非event.target.value

import { useState } from 'react'; function CheckboxExample() { const [isChecked, setIsChecked] = useState(false); return ( <div> <input id="checkbox-input" type="checkbox" checked={isChecked} onChange={(event) => setIsChecked(event.target.checked)} /> <label htmlFor="checkbox-input">Agree to terms and conditions</label> <p>Checkbox is {isChecked ? 'checked' : 'unchecked'}</p> </div> ); }

单选组同理:用同一个name属性(如name="gender")保证互斥,每个选项用checked={gender === 'male'}这类比较决定选中态,onChange更新为event.target.value

各类受控输入的完整对照(来自手册汇总表):

输入类型关键元素关键值属性状态更新
文本输入<input>valuesetState(event.target.value)
复选框输入<input>checkedsetState(event.target.checked)
单选组<input>checkedsetState(event.target.value)
文本区域<textarea>valuesetState(event.target.value)
选择下拉菜单<select>valuesetState(event.target.value)

注意<textarea>与 HTML 不同:React 用value属性控制,而不是在标签内写文本。

文本输入还会考type属性的选用,手册给出的对照表:

type用途内置校验
text常规文本输入
number数字文本输入有,仅允许数字,可配合min/max
email电子邮件地址有,必须包含@
password安全密码输入无,但输入会被掩码
search带清除按钮的搜索字段
tel电话号码无,可配合pattern
urlURL有,必须以http://https://开头
datetime-local日期和时间选择
color颜色选择器

非受控写法的配套要求

非受控表单的值走 DOM,因此 Signup Form 文档中的几条 HTML 要求直接决定它能不能工作:

  • name属性是表单数据的键:没有name<input>不会出现在提交数据中,formData.get('name')也取不到对应值;namevalue配对后才会进入 HTTP 请求;
  • 每个输入配一个<label>:通过htmlFor+id关联(或把<input>包在<label>里)。placeholder 不是 label——用户输入后会消失,屏幕阅读器也可能跳过;
  • 控件包在<form>:这样才能获得浏览器的回车提交行为,并在onSubmit中拿到包含所有字段数据的event.target

面试追问怎么兑现

输入时实时校验

受控组件做实时校验最省事:把错误信息也放进 state,提交(或每次输入)时检查,失败时渲染错误文案。手册示例(在其基础上补了文档省略的useStateimport):

import { useState } from 'react'; function EmailValidationForm() { const [email, setEmail] = useState(''); const [error, setError] = useState(''); const validateEmail = (email) => { return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email); }; function handleSubmit(event) { event.preventDefault(); if (!validateEmail(email)) { setError('Please enter a valid email address'); return; } setError(''); alert(`Valid Email: ${email}`); } return ( <form onSubmit={handleSubmit}> <label> Email: <input type="email" value={email} onChange={(event) => setEmail(event.target.value)} /> </label> {error && <p style={{ color: 'red' }}>{error}</p>} <button type="submit">Submit</button> </form> ); }

表单重置

对照表里的重置行就是两种实现的分叉点:

  • 受控:直接setState("")(多字段可用useReducerRESETaction 原子化重置全部字段);
  • 非受控:写ref.current.value = "",或触发'reset'事件。

这正是"默认受控"建议的由来:重置是高频 follow-up,受控下是一行 state 更新,非受控下要逐个处理 ref。

HTML5 原生校验:两种实现都可以叠加

requiredminLengthpattern等属性由浏览器在提交前校验,无需 JavaScript,能减少因无效输入引起的重渲染。非受控表单里尤其常用,因为浏览器会替你挡掉第一次校验:

function HTML5ValidationForm() { return ( <form> <label> Email: <input type="email" required /> </label> <br /> <label> Password (Min 6 characters): <input type="password" minLength="6" required /> </label> <br /> <label> Phone (Numbers only): <input type="tel" pattern="[0-9]{10}" required /> </label> <br /> <button type="submit">Submit</button> </form> ); }

但手册明确警告:客户端校验只是体验层面的便利,不是安全边界。攻击者可以完全绕过你的 React 表单直接请求接口,任何你在意的约束(必填、长度、格式、唯一性)都必须服务端重查。手册给出的服务端校验示例是向/api/validate-emailfetch()请求,response.ok为假时抛data.message,在catchsetError(err.message)并渲染到表单下方。这条"服务端必须再校验一次"的结论在 Signup Form 文档中同样出现,两篇文档口径一致。

可选分支:允许第三方库时使用 React Hook Form

文档说明,若题目允许第三方库(如 take-home 作业),React Hook Form 是手册推荐的补充,它用 refs 而非 state 来减少重渲染,并内置校验支持。动态字段用useFieldArray处理。Formik 被文档标注为 unmaintained,不建议作为面试选型。带校验的注册写法:

import { useForm } from 'react-hook-form'; function HookFormValidation() { const { register, handleSubmit, formState: { errors }, } = useForm(); const onSubmit = (data) => alert(JSON.stringify(data)); return ( <form onSubmit={handleSubmit(onSubmit)}> <label> Email: <input type="email" {...register('email', { required: 'Email is required' })} /> </label> {errors.email && <p style={{ color: 'red' }}>{errors.email.message}</p>} <label> Password: <input type="password" {...register('password', { required: 'Password is required', minLength: { value: 6, message: 'Password must be at least 6 characters', }, })} /> </label> {errors.password && ( <p style={{ color: 'red' }}>{errors.password.message}</p> )} <button type="submit">Submit</button> </form> ); }

错误信息统一从formState.errors读取并渲染。

提交后如何核对结果

手册示例自带的核对方式就是提交处理程序里的输出,可直接用来验证两条实现路径是否等价:

  • 受控表单:提交后alert(Submitted Name: ${name})弹出 state 中的值(文档示例输出,值取决于你输入的内容);
  • 非受控表单:console.log会先后打印formData.get('name')nameRef.current.value,两者应一致且等于你输入的值。

如果非受控表单里formData.get('name')拿到的是null,先检查<input>是否漏了name属性——没有name的输入项不会进入FormData,也不会进入提交数据,这是 Signup Form 文档明确指出的行为。

继续练习

手册在 Forms 章节末尾列出的编码练习题可直接用来验证本文的选择路径:Contact Form(联系表单)、Auth Code Input(身份验证代码输入)、Generate Table(生成表格)、Mortgage Calculator(抵押贷款计算器)、Temperature Converter(温度转换器)、Flight Booker(航班预订器)。按"字段少、可能需要实时校验"的表单题,默认走受控;只有题目强调大表单性能或文件上传时才考虑非受控。

延伸阅读:Forms in React Interviews、Signup Form Example in React。

【免费下载链接】front-end-interview-handbookFront End interview preparation materials for busy engineers (updated for 2026)项目地址: https://gitcode.com/GitHub_Trending/fr/front-end-interview-handbook

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/11 17:53:24

智能水利平台的高并发实时调度与水质分析工程实践

我之前做过不少水利相关的数据项目&#xff0c;但像这次这样把高并发实时调度和水质分析揉在同一个平台里的&#xff0c;还真是头一回。整个项目从需求梳理到落地&#xff0c;踩了不少坑&#xff0c;也沉淀了些可复用的思路。今天就把奥斯陆这个智能水利场景下的工程设计实践&a…

作者头像 李华
网站建设 2026/9/11 17:48:14

Maestro 从零到实战:用 YAML 测试流搞定 UI 自动化测试

Maestro 从零到实战&#xff1a;用 YAML 测试流搞定 UI 自动化测试 【免费下载链接】Maestro Painless E2E Automation for Mobile and Web 项目地址: https://gitcode.com/GitHub_Trending/ma/Maestro 还在手动点按、逐帧截图做回归吗&#xff1f;Maestro 是开源 UI 自…

作者头像 李华
网站建设 2026/9/11 17:47:32

点读笔素材制作:BNL转TNB格式转换与易读宝魔术贴工厂实战

简介&#xff1a;易读宝魔术贴教程及全套工具是一套面向电商卖家及有声内容制作者的实用资源&#xff0c;旨在帮助用户自行制作有声教程&#xff0c;解决魔术贴格式转换&#xff08;如BNL转TNB&#xff09;的常见问题。压缩包共收录1210个文件&#xff0c;以QML界面组件、DLL动…

作者头像 李华
网站建设 2026/9/11 17:43:45

【k8s】从 GitLab 504 到 Ceph 存储问题

详细排查与操作全过程复盘 以下是本次从 GitLab 504 到 Ceph 存储清理 的完整操作流程&#xff0c;每一步均包含&#xff1a;操作背景、具体命令、预期结果、实际结果及结论。阶段一&#xff1a;问题感知与初步定位&#xff08;GitLab 504&#xff09; 步骤 1&#xff1a;确认 …

作者头像 李华
网站建设 2026/9/11 17:41:06

2026年9月从行业视角看喀什地区辨别镜片品牌授权店真假的细节

在喀什地区辨别镜片品牌授权店的真假&#xff0c;核心不在于门店招牌的大小或装修的档次&#xff0c;而在于授权链条是否完整可查、验光流程是否规范到位、售后体系是否形成闭环这三个硬指标。2026年9月&#xff0c;镜片品牌方对渠道管理的持续收紧&#xff0c;让消费者手中可用…

作者头像 李华