710 字
4 分钟
React 受控组件与非受控组件

一句话区别#

受控:你用 state 决定输入框现在显示什么。非受控:输入框自己管自己的值,你只在需要时去读。

受控:value 加 onChange#

function Form() {
const [firstName, setFirstName] = useState('')
return (
<input
value={firstName}
onChange={e => setFirstName(e.target.value)}
/>
)
}

官方规则很硬:传了 value(复选框、单选框则传 checked),就必须同时给 onChange。缺了它,输入框会变成只读,控制台还会报一条错,因为你等于每次都把它强行设回同一个值。

什么时候值得受控:你要在每次输入时更新界面的其他部分(实时预览、字符计数、格式校验),或者除输入框之外还有别的途径改这个值,比如旁边一个「+10 岁」按钮。

非受控:defaultValue 加提交时读取#

function Form() {
function handleSubmit(e) {
e.preventDefault()
const formData = new FormData(e.target)
console.log(Object.fromEntries(formData.entries()))
}
return (
<form onSubmit={handleSubmit}>
<input name="firstName" defaultValue="Taylor" />
<input name="age" type="number" />
<button type="submit">提交</button>
</form>
)
}

两个要点。初始值用 defaultValue(复选框用 defaultChecked),不要写成 value。每个 <input> 都要有 name,FormData 拿它当键。

只在提交时取一次数据的表单适合这种写法:字段再多也不用为每个字段建 state。

三条容易踩的规则#

两种模式不能混,也不能中途切换。value 从 undefined 变成字符串时,React 会报「A component is changing an uncontrolled input to be controlled」。初值给空字符串 '',不要给 null 或 undefined。

复选框读 e.target.checked,不是 e.target.value。

<form> 里那个不带 type 的 <button> 会提交表单。自定义按钮组件记得写 type="button",否则它会意外提交。

输入时的重渲染怎么控#

受控输入每敲一个键都会更新 state,装着这个 state 的组件就会重渲染。把输入框的状态关进它自己的小组件,别让整页跟着动:

function App() {
return (
<>
<SignupForm />
<PageContent />
</>
)
}

PageContent 不依赖输入值,把它挪到状态外面,敲键时就只有 SignupForm 重渲染。

如果别的部分确实要用这个值(搜索框与结果列表),用 useDeferredValue 让输入保持跟手。

顺带把标签关联上#

每个输入框都该有标签。把 <input> 包进 <label> 最省事;两者分开写时,用 htmlFor 配 id,id 交给 useId 生成,避免同一组件渲染多次时撞车。

文件输入只能非受控#

<input type="file" /> 的值由浏览器管理,不能用 state 设定,只能读:

function Upload() {
function handleSubmit(e) {
e.preventDefault()
const file = new FormData(e.target).get('avatar')
console.log(file)
}
return (
<form onSubmit={handleSubmit}>
<input type="file" name="avatar" />
<button type="submit">上传</button>
</form>
)
}

怎么选#

需要实时反应、或有多条改值途径,就用受控。只在提交时取一次,就用非受控。

React 19 还给了第三条路:<form action={fn}> 把 FormData 直接交给动作函数,提交成功后表单自动重置。细节见 React 的表单 Actions。

第 7 篇,共 39 篇本文属于系列

React 受控组件与非受控组件
https://www.choria.top/posts/control/
作者
乔瑞雅·克林威尔
发布于
2026-05-03
许可协议
CC BY-NC-SA 4.0

分享文章

生成精美分享图或复制链接,与更多人分享本文。

继续阅读

沿着主题读

基于共同的标签与分类

换条路线

从其他文章中稳定抽取

最后更新于 ,距今已过 189 天

部分内容可能已过时