React 应用程序中的事件 "OnkeyDown" 不同步

Event "OnkeyDown" out of sync in the react app

大家好。我是网络开发新手。我只是不明白为什么第二次触发事件“OnKeyDown”。我的任务是在输入字段中更改(增加、减少)部分日期(日、月、年、小时、分钟、秒)。

应用程序功能:

  1. 当按下按钮组合'ctrl'+'ArrowUp'时,日期的一部分随着其他部分的可变而改变。 例如,如果当前日期是 2021 年 12 月 31 日,而您将第 31 天更改为第 1 天,则日期将更改为 2022 年 1 月 1 日。
  2. 当按下“ArrowUp”按钮时,只改变这部分,日期的其他部分是不可变的。

功能的第一步工作正常,现在我创建第二步。我的问题是当我想通过按第二次触发的“ArrowUp”事件来增加天数时,这就是不同步的原因。希望我能够正确解释。

Try my App in sandbox snippet

代码中有问题的部分:

增加日期天数

if (e.key === "ArrowUp") {
      if (
        inputEl.current.selectionStart >= 0 &&
        inputEl.current.selectionStart <= 2 
      ) {
        e.preventDefault();
        let nextMilisec = milisec + 86400000;
        setSelection({ start: 0, end: 2 });
        setMilisec(nextMilisec);
        setDate((prevState) => {
          return {
            ...prevState,
            day: new Date(nextMilisec).getDate(),
          };
        });
        cur_date.setDate(day);
        setValue(date_format_unmut);
      }

减少日期天数

if (
        inputEl.current.selectionStart >= 0 &&
        inputEl.current.selectionStart <= 2 
      ) {
        e.preventDefault();
        let nextMilisec = milisec - 86400000;
        setSelection({ start: 0, end: 2 });
        setMilisec(nextMilisec);
        setDate((prevState) => {
          return {
            ...prevState,
            day: new Date(nextMilisec).getDate(),
          };
        });
        cur_date.setDate(day);
        setValue(date_format_unmut);
      }

完整代码(抱歉,我知道很多,我需要重构这个烂摊子。)

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 dayNames = {
  0: "Sunday",
  1: "Monday",
  2: "Tuesday",
  3: "Wednesday",
  4: "Thursday",
  5: "Friday",
  6: "Saturday",
};

const monthNames = {
  0: "January",
  1: "February",
  2: "March",
  3: "April",
  4: "May",
  5: "June",
  6: "July",
  7: "August",
  8: "September",
  9: "October",
  10: "November",
  11: "December",
};

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_unmut = `${("0" + day).slice(-2)}/${
    monthNames[month]
  }/${year} ${("0" + hour).slice(-2)}:${("0" + minute).slice(-2)}:${(
    "0" + second
  ).slice(-2)}`;

  const inputEl = useRef();
  const [selection, setSelection] = useState();

  const date_format = cur_date.toLocaleString().split(".");
  const monthIndex = +date_format[1];
  date_format[1] = monthNames[monthIndex - 1];
  let string_date_format = date_format.join("/").replace(",", "");

  const [value, setValue] = useState(string_date_format);

  useEffect(() => {
    if (!selection) return; // prevent running on start
    const { start, end } = selection;
    inputEl.current.focus();
    inputEl.current.setSelectionRange(start, end);
  }, [selection]);

  const keyHandler = (e) => {
    if (e.key === "ArrowUp") {
      if (
        inputEl.current.selectionStart >= 0 &&
        inputEl.current.selectionStart <= 2 
      ) {
        e.preventDefault();
        let nextMilisec = milisec + 86400000;
        setSelection({ start: 0, end: 2 });
        setMilisec(nextMilisec);
        setDate((prevState) => {
          return {
            ...prevState,
            day: new Date(nextMilisec).getDate(),
          };
        });
        cur_date.setDate(day);
        setValue(date_format_unmut);
      }
      if (
        inputEl.current.selectionStart >= 3 &&
        inputEl.current.selectionStart <= date_format[1].length + 3 
      ) {
        e.preventDefault();
        const newMonth = cur_date.getMonth() + 1;
        cur_date.setMonth(newMonth);
        console.log(cur_date);
        setSelection({
          start: 3,
          end: date_format[1].length + 3,
        });
        setDate((prevState) => {
          return {
            ...prevState,
            month: cur_date.getMonth() - 1,
          };
        });

        setValue(date_format_unmut);
      }
    }

    if (e.ctrlKey && e.key === "ArrowUp") {
      if (
        inputEl.current.selectionStart >= 0 &&
        inputEl.current.selectionStart <= 1
      ) {
        e.preventDefault();
        setSelection({ start: 0, end: 2 });
        const newDay = cur_date.setDate(cur_date.getDate() + 1);
        const increaseDayData = new Date(newDay).toLocaleString().split(".");
        const monthIndex = +increaseDayData[1];
        increaseDayData[1] = monthNames[monthIndex - 1];
        const addDay_date_format = increaseDayData.join("/").replace(",", "");
        setValue(addDay_date_format);
      }
      if (
        inputEl.current.selectionStart >= 3 &&
        inputEl.current.selectionStart <= date_format[1].length + 3
      ) {
        const newMonth = cur_date.setMonth(cur_date.getMonth() + 1);
        const increaseMonthData = new Date(newMonth)
          .toLocaleString()
          .split(".");
        const monthNumber = increaseMonthData[1];
        setSelection({ start: 3, end: monthNames[monthNumber - 1].length + 3 });
        const monthIndex = +increaseMonthData[1];
        increaseMonthData[1] = monthNames[monthIndex - 1];
        const addMonth_date_format = increaseMonthData
          .join("/")
          .replace(",", "");
        setValue(addMonth_date_format);
      }
      if (
        inputEl.current.selectionStart >= date_format[1].length + 4 &&
        inputEl.current.selectionStart <= date_format[1].length + 8
      ) {
        const newYear = cur_date.setFullYear(cur_date.getFullYear() + 1);
        const increaseYearData = new Date(newYear).toLocaleString().split(".");
        setSelection({
          start: date_format[1].length + 4,
          end: date_format[1].length + 8,
        });
        const monthIndex = +increaseYearData[1];
        increaseYearData[1] = monthNames[monthIndex - 1];
        const addYear_date_format = increaseYearData.join("/").replace(",", "");
        setValue(addYear_date_format);
      }
      if (
        inputEl.current.selectionStart >= date_format[1].length + 9 &&
        inputEl.current.selectionStart <= date_format[1].length + 11
      ) {
        const newHour = cur_date.setHours(cur_date.getHours() + 1);
        const increaseHourData = new Date(newHour).toLocaleString().split(".");
        setSelection({
          start: date_format[1].length + 9,
          end: date_format[1].length + 11,
        });
        const monthIndex = +increaseHourData[1];
        increaseHourData[1] = monthNames[monthIndex - 1];
        const addHour_date_format = increaseHourData.join("/").replace(",", "");
        setValue(addHour_date_format);
      }
      if (
        inputEl.current.selectionStart >= date_format[1].length + 12 &&
        inputEl.current.selectionStart <= date_format[1].length + 14
      ) {
        const newMinutes = cur_date.setMinutes(cur_date.getMinutes() + 1);
        const increaseMinutesData = new Date(newMinutes)
          .toLocaleString()
          .split(".");
        setSelection({
          start: date_format[1].length + 12,
          end: date_format[1].length + 14,
        });
        const monthIndex = +increaseMinutesData[1];
        increaseMinutesData[1] = monthNames[monthIndex - 1];
        const addMinutes_date_format = increaseMinutesData
          .join("/")
          .replace(",", "");
        setValue(addMinutes_date_format);
      }
      if (
        inputEl.current.selectionStart >= date_format[1].length + 15 &&
        inputEl.current.selectionStart <= date_format[1].length + 17
      ) {
        const newSeconds = cur_date.setSeconds(cur_date.getSeconds() + 1);
        const increaseSecondsData = new Date(newSeconds)
          .toLocaleString()
          .split(".");
        setSelection({
          start: date_format[1].length + 15,
          end: date_format[1].length + 17,
        });
        const monthIndex = +increaseSecondsData[1];
        increaseSecondsData[1] = monthNames[monthIndex - 1];
        const addSeconds_date_format = increaseSecondsData
          .join("/")
          .replace(",", "");
        setValue(addSeconds_date_format);
      }
    }
    if (e.key === "ArrowDown") {
      if (
        inputEl.current.selectionStart >= 0 &&
        inputEl.current.selectionStart <= 2 
      ) {
        e.preventDefault();
        let nextMilisec = milisec - 86400000;
        setSelection({ start: 0, end: 2 });
        setMilisec(nextMilisec);
        setDate((prevState) => {
          return {
            ...prevState,
            day: new Date(nextMilisec).getDate(),
          };
        });
        cur_date.setDate(day);
        setValue(date_format_unmut);
      }
    }
    if (e.ctrlKey && e.key === "ArrowDown") {
      e.preventDefault();
      if (
        inputEl.current.selectionStart >= 0 &&
        inputEl.current.selectionStart <= 1
      ) {
        setSelection({ start: 0, end: 2 });
        const newDay = cur_date.setDate(cur_date.getDate() - 1);
        const increaseDayData = new Date(newDay).toLocaleString().split(".");
        const monthIndex = +increaseDayData[1];
        increaseDayData[1] = monthNames[monthIndex - 1];
        const addDay_date_format = increaseDayData.join("/").replace(",", "");
        setValue(addDay_date_format);
      }
      if (
        inputEl.current.selectionStart >= 3 &&
        inputEl.current.selectionStart <= date_format[1].length + 3
      ) {
        const newMonth = cur_date.setMonth(cur_date.getMonth() - 1);
        const increaseMonthData = new Date(newMonth)
          .toLocaleString()
          .split(".");
        const monthNumber = increaseMonthData[1];
        setSelection({ start: 3, end: monthNames[monthNumber - 1].length + 3 });
        const monthIndex = +increaseMonthData[1];
        increaseMonthData[1] = monthNames[monthIndex - 1];
        const addMonth_date_format = increaseMonthData
          .join("/")
          .replace(",", "");
        setValue(addMonth_date_format);
      }
      if (
        inputEl.current.selectionStart >= date_format[1].length + 4 &&
        inputEl.current.selectionStart <= date_format[1].length + 8
      ) {
        const newYear = cur_date.setFullYear(cur_date.getFullYear() - 1);
        const increaseYearData = new Date(newYear).toLocaleString().split(".");
        setSelection({
          start: date_format[1].length + 4,
          end: date_format[1].length + 8,
        });
        const monthIndex = +increaseYearData[1];
        increaseYearData[1] = monthNames[monthIndex - 1];
        const addYear_date_format = increaseYearData.join("/").replace(",", "");
        setValue(addYear_date_format);
      }
      if (
        inputEl.current.selectionStart >= date_format[1].length + 9 &&
        inputEl.current.selectionStart <= date_format[1].length + 11
      ) {
        const newHour = cur_date.setHours(cur_date.getHours() - 1);
        const increaseHourData = new Date(newHour).toLocaleString().split(".");
        setSelection({
          start: date_format[1].length + 9,
          end: date_format[1].length + 11,
        });
        const monthIndex = +increaseHourData[1];
        increaseHourData[1] = monthNames[monthIndex - 1];
        const addHour_date_format = increaseHourData.join("/").replace(",", "");
        setValue(addHour_date_format);
      }

      if (
        inputEl.current.selectionStart >= date_format[1].length + 12 &&
        inputEl.current.selectionStart <= date_format[1].length + 14
      ) {
        const newMinutes = cur_date.setMinutes(cur_date.getMinutes() - 1);
        const increaseMinutesData = new Date(newMinutes)
          .toLocaleString()
          .split(".");
        setSelection({
          start: date_format[1].length + 12,
          end: date_format[1].length + 14,
        });
        const monthIndex = +increaseMinutesData[1];
        increaseMinutesData[1] = monthNames[monthIndex - 1];
        const addMinutes_date_format = increaseMinutesData
          .join("/")
          .replace(",", "");
        setValue(addMinutes_date_format);
      }
      if (
        inputEl.current.selectionStart >= date_format[1].length + 15 &&
        inputEl.current.selectionStart <= date_format[1].length + 17
      ) {
        const newSeconds = cur_date.setSeconds(cur_date.getSeconds() - 1);
        const increaseSecondsData = new Date(newSeconds)
          .toLocaleString()
          .split(".");
        setSelection({
          start: date_format[1].length + 15,
          end: date_format[1].length + 17,
        });
        const monthIndex = +increaseSecondsData[1];
        increaseSecondsData[1] = monthNames[monthIndex - 1];
        const addSeconds_date_format = increaseSecondsData
          .join("/")
          .replace(",", "");
        setValue(addSeconds_date_format);
      }
    }
  };

  const fullDateScreen = cur_date.toLocaleString().split(" ");
  const timeScreen = fullDateScreen[1];
  const dateScreen = fullDateScreen[0];
  const dayScreen = dayNames[cur_date.getDay()];
  const monthScreen = monthNames[cur_date.getMonth()];
  const dateScreenFormat = dateScreen.replaceAll(".", " ");
  const firstNumber = dateScreenFormat[0];
  const smallDateFormat =
    dayScreen + "," + " " + dateScreenFormat.slice(1, -8) + " " + monthScreen;
  const bigDateFormat =
    dayScreen + "," + " " + dateScreenFormat.slice(0, -8) + " " + monthScreen;

  const changeHandler = (e) => {
    setValue(e.target.value);
  };

  return (
    <div className={styles.content}>
      <div className={styles.inputDublicat}>
        <h1>{timeScreen.slice(0, 5)}</h1>
        <p>{firstNumber == 0 ? smallDateFormat : bigDateFormat}</p>
      </div>
      <div className={styles.bodyFlex}>
        <div className={styles.main}>
          <div>
            <h1 className={styles.title}> Frontend Task</h1>
            <input
              ref={inputEl}
              value={value}
              onChange={changeHandler}
              onKeyDown={keyHandler}
            />

            <div className={styles.textBlock}>
              <div className={styles.instruction}>
                <p>
                  You can change input parts of date
                  (day,month,year,hour,minutes,seconds) by pressing buttons or
                  combination. The cursor must be on the focus in the input
                  field.
                </p>
              </div>
              <div>
                <h3>Buttons </h3>
                <p>&uarr; - unmutable date</p>
                <p>&darr; - unmutable date</p>
              </div>
              <div>
                <h3>Combinations</h3>
                <p>"CTRL" + &uarr; - mutable date</p>
                <p>"CTRL" + &darr; - unmutable date</p>
              </div>
            </div>
          </div>
        </div>
        <div>
          <p className={styles.name}>DAVID ABRAMOV</p>
          <hr width="450%" className={styles.line} />
        </div>
      </div>
    </div>
  );
};

export default DataInput;

我猜您收到了两个 keyDown 事件,因为“ctrl”和“向上箭头”键都会触发它们自己单独的 keyDown 事件。

检查这一点的最简单方法是观察 e.key == "arrowUp",然后检查事件的修饰符状态以查看是否也按下了“Control”。

(您还应确认该组合键未被其他功能阻止;在我的笔记本电脑上按 ctrl-arrow-up 启动 OSX 的 Mission Control 应用程序,老实说,我不记得是否这是默认组合键或我设置但忘记的东西)

(注意键名是not standardized,其他情况下需要构建跨平台跨浏览器支持。)

document.getElementById("foo").addEventListener("keydown", e => {
  console.log("Event key: ", e.key)
  console.log("Control modifier:", e.getModifierState("Control"))
  
  if (e.key === "ArrowUp" && e.getModifierState("Control")) {
    console.log("Ctrl-arrow-up pressed");
  }
})
Type in here: <input id="foo">