Home > Net >  Event "OnkeyDown" out of sync in the react app
Event "OnkeyDown" out of sync in the react app

Time:11-09

Hello everyone. I'm new in web dev. I just can't figure out why event "OnKeyDown" is triggered from second time. My task is change (encrease, decrease) parts of date(day,month,year,hour,minutes,seconds) in input field.

App Functionality:

  1. When combination of buttons 'ctrl' 'ArrowUp' pressed, parts of date changed with mutable of other parts. For example, if current date 31/12/2021 and you change days 31 to 01, date changes to 01/01/2022.
  2. When button "ArrowUp" pressed, change only this part and other parts of date is unmutable.

First step of functionality work fine, and now i create second step. My problem is when i want to increase days by pressing "ArrowUp" event fired from second time and this is reason desync. Hope I was able to explain correctly.

Try my App in sandbox snippet

Problematic parts of code:

increase days of date

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);
      }

decrease days of date

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);
      }

Full Code (Sorry, i know it's a lot and i need refactoring of this mess.)

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;

CodePudding user response:

I'm guessing you're getting two keyDown events because both the "ctrl" and the "up arrow" keys will fire their own separate keyDown events.

The easiest way to check for this would be to watch for e.key == "arrowUp" and then check the event's modifier state to see if "Control" is also pressed.

(You should also confirm that that key combination isn't blocked by some other function; on my laptop ctrl-arrow-up launches OSX's Mission Control app, I honestly don't recall whether that's a default key combo or something I set up and forgot about)

(Note that key names are not standardized, you will need to build in other cases for cross-platform and cross-browser support.)

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">
<iframe name="sif1" sandbox="allow-forms allow-modals allow-scripts" frameborder="0"></iframe>

  • Related