React日期输入onChange函数触发两次并返回NaN-NaN-NaN问题排查
嘿,我帮你定位到了问题的核心,主要是两个小错误导致了这个异常情况,咱们一步步来解决:
问题根源
1. HTML日期格式转换顺序搞反了
HTML的<input type="date">只认yyyy-MM-dd(年-月-日)格式,但你的convertDate函数在转成HTML格式时,错误地把「日」放在了「月」的前面:
// 错误的代码 if (format === "html") date = yyyy + "-" + dd + "-" + mm;
举个例子,原日期是24.03.2000(日.月.年),按这个代码会生成2000-24-03——这显然是无效日期(哪来的24月?),浏览器识别不了就会自动清空输入框,触发第二次onChange事件,此时输入值为空,转换时new Date("")会得到Invalid Date,最终就输出了NaN-NaN-NaN。
2. 没处理无效日期的边界情况
当输入的日期无效时,new Date()返回的是Invalid Date,后续调用getDate()、getMonth()这些方法都会返回NaN,自然就生成了错误的格式。
解决方案
步骤1:修正日期转换顺序,优化Date对象创建
直接用new Date(dd.mm.yyyy)可能会有跨浏览器解析问题,咱们手动拆分日期部分,再创建Date对象(注意JavaScript的月份是从0开始的哦),同时把HTML格式的转换改成正确的年-月-日顺序:
ConvertDate(inputDate, format) { let date; // 根据输入格式处理Date对象的创建 if (format === "html") { // 输入是yyyy-mm-dd,直接转Date date = new Date(inputDate); } else if (format === "graphql") { // 输入是dd.mm.yyyy,手动拆分 const [day, month, year] = inputDate.split("."); date = new Date(year, month - 1, day); // 月份减1,因为JS的月份是0-based } else { date = new Date(inputDate); } // 先检查日期是否有效 if (isNaN(date.getTime())) { return ""; // 或者返回你需要的默认值 } const dd = String(date.getDate()).padStart(2, "0"); const mm = String(date.getMonth() + 1).padStart(2, "0"); const yyyy = date.getFullYear(); if (format === "graphql") { return `${dd}.${mm}.${yyyy}`; } else if (format === "html") { return `${yyyy}-${mm}-${dd}`; // 这里改成正确的yyyy-mm-dd顺序 } return date.toString(); }
这里用padStart(2, "0")来补前导零,比之前的if判断更简洁。
步骤2:改用受控组件替代defaultValue
defaultValue属于非受控组件写法,容易出现状态和输入框值不一致的情况,咱们改成受控组件,用value绑定转换后的HTML日期:
render() { // 提前把GraphQL格式的日期转成HTML需要的格式 const htmlBirthDate = this.convertDate(this.state.newUser.geburtsdatum, "html"); return ( // 其他代码... <input type="date" name="geburtsdatum" value={htmlBirthDate} onChange={this.onChange} placeholder={this.getDJHTooltip("geburtsdatum")} /> // 其他代码... ); }
这样输入框的值完全由React状态控制,不会出现浏览器自动清空的情况,也就不会触发第二次onChange了。
步骤3:优化onChange的处理逻辑
在onChange里,先判断输入值是否为空,再进行转换,避免无效值进入状态:
onChange = e => { let value = e.target.value; const name = e.target.name; if (e.target.type === "date") { // 只有当输入值不为空时才转换格式 value = value ? this.convertDate(value, "graphql") : ""; console.log(value, name); } this.setState(prevState => ({ newUser: { ...prevState.newUser, [name]: value } })); };
测试验证
现在再测试之前有问题的日期:
- 对于
24.03.2000,转换为HTML格式是2000-03-24,浏览器可以正常识别,不会触发第二次onChange; - 保存时转回GraphQL格式,得到的还是
24.03.2000,完全符合要求。
这样就能彻底解决双触发和NaN的问题啦!
内容的提问来源于stack exchange,提问作者Musawar

