React输入框按ArrowUp调整日期时光标回退、选中状态失效问题
问题修复方案
核心问题原因
- 未阻止ArrowUp按键的默认行为:按下上箭头时浏览器默认会将输入框光标移动到内容起始位置,直接覆盖了你手动设置的选中状态
- 日期计算存在闭包陷阱:
setDate中直接读取的milisec是当前渲染周期的旧值,没有使用更新后的毫秒数计算日期 - 原
selection状态判断逻辑有漏洞:空对象在JS中属于truthy值,初始判断if (!selection) return不会生效,且监听依赖缺少输入框格式化后的日期值,会导致选中时机和输入框值更新时机不同步
修复步骤
1. 阻止按键默认行为
在满足触发条件的分支中第一行添加e.preventDefault(),禁用浏览器默认的光标移动逻辑。
2. 修复日期计算的闭包问题
将date状态的更新放到setMilisec的回调函数中,使用最新的毫秒值计算日期,避免读取到旧的状态。
3. 修正选中逻辑的判断和依赖
调整selection的非空判断逻辑,同时在useEffect依赖中添加格式化后的日期值,确保输入框内容更新完成后再设置选中范围。
修改后的完整代码
import React, { useState, useEffect, useRef } from "react"; import styles from "./DataInput.module.css"; const cur_date = new Date(); const def_day = cur_date.getDate(); const def_month = cur_date.getMonth(); const def_year = cur_date.getFullYear(); const def_hour = cur_date.getHours(); const def_minute = cur_date.getMinutes(); const def_seconds = cur_date.getSeconds(); const DataInput = () => { const [milisec, setMilisec] = useState(cur_date.valueOf()); const [date, setDate] = useState({ day: def_day, month: def_month, year: def_year, hour: def_hour, minute: def_minute, second: def_seconds, }); const { day, month, year, hour, minute, second } = date; const date_format = `${("0" + day).slice( -2 )}/${month}/${year} ${hour}:${minute}:${second}`; const inputEl = useRef(); const [selection, setSelection] = useState({}); useEffect(() => { // 修正空对象判断逻辑,增加date_format依赖确保内容更新后再设置选中 if (!Object.keys(selection).length) return; const { start, end } = selection; inputEl.current.focus(); inputEl.current.setSelectionRange(start, end); }, [selection, date_format]); const keyHandler = (e) => { if (e.code === "ArrowUp" && inputEl.current.selectionStart === 1) { // 阻止浏览器默认光标移动行为 e.preventDefault(); setSelection({ start: 0, end: 2 }); setMilisec((prevMilisec) => { const newMilisec = prevMilisec + 86400000; // 在setMilisec回调中用最新值计算日期,避免闭包问题 setDate((prevState) => { return { ...prevState, day: new Date(newMilisec).getDate() }; }); return newMilisec; }); } }; const changeHandler = (e) => { // 后续如果需要支持手动修改输入内容,可在此处补全字符串解析更新状态的逻辑 }; return ( <div className={styles.main}> <h1> Frontend Task</h1> <p id="name">Abramov David</p> <input ref={inputEl} type="text" value={date_format} onChange={changeHandler} onKeyDown={keyHandler} /> </div> ); }; export default DataInput;
内容的提问来源于stack exchange,提问作者David Abramov
相关产品推荐
相关产品推荐

