先记住这个答案
数字控件编辑时仍有字符串形式的 value,用户清空会得到空字符串。如果每次输入立刻 Number(value),空字符串会变成零,界面就可能无法保持清空;使用 valueAsNumber 时,空值或无法转换的输入可能得到 NaN。可以用字符串 state 保存编辑值,提交或失焦时明确判断空值、格式和范围,再得到业务数字。不要把 NaN 当作正常受控值传回输入框。
- 空输入、零和非法数字必须分别表达
- 编辑值可用字符串保存,业务数值在边界校验
- type number 不会自动完成全部业务范围与精度规则
为什么输入值不能直接等同于最终数字
用户把数量从十二改成三时,可能先全选删除,再输入新值。中间的空白是正常编辑过程,并不一定表示用户选择了零。如果在每次 onChange 中把空字符串转换成零,再把零作为 value 传回去,就打断了这个编辑过程。
valueAsNumber 提供数值读取方式,但空白或无法转换时返回 NaN,需要显式处理。不同浏览器对 number 输入中未完成的指数、负号或小数表示也有自己的编辑行为。不要宣称用 type number 就能保留所有任意字符串中间态,复杂格式化输入需要专门设计。
保存草稿字符串,提交时再检查
下面组件始终用字符串维护 draft,清空后保持空值。提交时先阻止默认行为,再区分必填空白、浏览器约束与有限数值,最后显示已确认数量。它演示的是从编辑状态到业务状态的边界,没有向服务器发送请求。
如果业务允许不填写,就把空值映射为明确的 null 或缺省字段,而不是默认映射为零。金额、精确小数或超出安全整数范围的数据,还需要额外精度方案;Number.isFinite 只能检查有限性,不能证明数值符合所有业务含义。
import { useState } from 'react';
export default function QuantityForm() {
const [draft, setDraft] = useState('');
const [message, setMessage] = useState('尚未提交');
function submit(event) {
event.preventDefault();
const input = event.currentTarget.elements.namedItem('quantity');
const amount = Number(draft);
if (draft === '' || !input.validity.valid || !Number.isFinite(amount)) {
setMessage('请输入 0 到 99 的整数');
return;
}
setMessage('已确认数量:' + amount);
}
return (
<form onSubmit={submit} noValidate>
<label>数量<input name="quantity" type="number" min="0" max="99"
step="1" required value={draft}
onChange={event => setDraft(event.target.value)} /></label>
<button type="submit">确认</button>
<p aria-live="polite">{message}</p>
</form>
);
}在 React 客户端运行后可输入并清空数量。空白、负数、超出 99 或不符合整数步进时显示提示,合法输入提交后显示确认数量;没有控制台输出。
外部回填和即时计算也需要区分
服务端返回数字时,可以在明确的初始化或切换对象流程中转换成编辑字符串。用户编辑期间若服务器推来新值,应决定覆盖、提示冲突还是保留草稿,不能因为数据已经是数字就忽略未保存输入。受控模式还应始终提供稳定类型,避免从 undefined 切换为字符串。
页面需要即时合计时,可以从 draft 推导一个带合法性状态的结果,例如空白时不显示合计,合法时再计算。不要为了展示方便额外保存一份可能与 draft 不一致的数字 state。错误提示也可区分正在编辑与正式提交,避免每次删除字符都立即宣布操作失败。
容易答错的地方
- Number 空字符串等于 NaN,所以直接转换没问题
- Number("") 会得到零,正是清空被自动填成零的常见原因。应先判断空值的业务含义,再进行数值转换。
- min、max 和 step 会自动夹紧所有输入
- 这些属性参与浏览器约束验证,不代表用户编辑时永远无法输入越界值。提交与服务端仍需校验,不能把显示控件的属性当成可信数据保证。
面试官还会怎么问?
用 parseInt 能解决小数和空值吗?
parseInt 可能截断小数或接受前缀后忽略剩余字符,未必符合数量字段规则。应先确定允许的格式,再采用相应转换与完整校验,不能用截断掩盖非法输入。
可以用 number 或空字符串的联合状态吗?
可以,只要所有分支都明确且不会把 NaN 传给 value。不过保留字符串草稿通常更容易区分编辑表示和最终数字,具体选择取决于允许的格式与交互。
为什么金额不宜只依赖普通浮点运算?
十进制金额可能无法由二进制浮点精确表示。应根据精度要求采用最小货币单位整数或合适的十进制方案,输入转换与存储计算都要使用一致规则。
参考资料
示例用于理解所注明的运行环境与边界;延伸学习可结合原文中的更多案例。