Next.js表单开发:是否该用useState?两段代码差异及选型
在Next.js中使用表单:useState管理输入值 vs 直接获取DOM值
先看你提供的两段代码:
非受控组件写法(不使用useState)
'use client'; export default function Page() { const handleSubmit = (e: React.FormEvent<HTMLFormElement>) => { e.preventDefault(); console.log(e.currentTarget.email.value); console.log(e.currentTarget.password.value); }; return ( <form onSubmit={handleSubmit}> <input type="text" name="email" /> <input type="password" name="password" /> <button type="submit">login</button> </form> ); }
受控组件写法(使用useState)
'use client'; import { useState } from 'react'; export default function Page() { const [email, setEmail] = useState(''); const [password, setPassword] = useState(''); const handleSubmit = (e: React.FormEvent<HTMLFormElement>) => { e.preventDefault(); console.log(email); console.log(password); }; return ( <form onSubmit={handleSubmit}> <input type="text" name="email" value={email} onChange={(e) => setEmail(e.target.value)} /> <input type="password" name="password" value={password} onChange={(e) => setPassword(e.target.value)} /> <button type="submit">login</button> </form> ); }
两种写法的核心差异
- 数据存储方式:
非受控组件的输入值存在DOM元素本身,只有提交时才通过e.currentTarget读取;受控组件的输入值存在React状态(useState)里,输入时实时更新状态,提交时直接用状态值。 - 实时交互能力:
受控组件能实时响应输入变化——比如输入时做格式校验(如邮箱格式检查)、禁用不符合要求的提交按钮、实时显示输入提示等;非受控组件做不到这些,只能在提交时一次性处理。 - 代码可维护性:
简单场景下非受控组件更简洁,但表单字段变多(比如添加用户名、手机号)或需要状态联动(如某个输入变化影响其他字段)时,受控组件的集中状态管理会更清晰,也更容易和React生态工具(如Formik、React Hook Form)集成。 - 符合React理念程度:
React是声明式框架,受控组件通过状态驱动UI,符合其设计思想;非受控组件依赖DOM操作,属于命令式写法,和React的声明式理念有冲突。
哪种写法更规范正确?
没有绝对的标准答案,取决于你的业务场景:
- 如果是极简表单(仅收集数据提交,无任何实时交互需求):两种写法都可行,非受控组件代码更短,上手更快。
- 如果是需要复杂交互的表单(实时验证、输入限制、多字段联动、状态共享):必须用受控组件,这是React官方推荐的方式,代码更可预测、易维护,也更贴合Next.js的客户端组件开发模式。
- 极端性能场景(比如表单有几十上百个字段):受控组件的频繁重渲染可能带来微小性能损耗,这时可以考虑非受控组件配合
useRef读取DOM值,但这种情况非常少见,大部分业务场景受控组件的性能完全够用。
另外在Next.js中,两种写法都能在客户端组件中正常运行,但受控组件更方便和Server Actions结合——你可以直接把状态值作为参数传给Server Action函数,无需额外读取DOM。
内容的提问来源于stack exchange,提问作者pvp11
相关产品推荐
相关产品推荐

