You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.27 22:15:03