0

I've used React create a time input component like this:

img

import { useEffect, useState } from "react";

function padLeadingZeros(num, size) {
  var s = num + "";
  while (s.length < size) s = "0" + s;
  return s;
}

function TimeInput({ hour, minute, onChange }) {
  const [time, setTime] = useState("");
  const [valuep, setValuep] = useState(0);

  const setFormatTime = (hour, minute) => {
    setTime(padLeadingZeros(hour, 2) + ":" + padLeadingZeros(minute, 2));
  }

  const handleAdd = () => {
    let [hour_str, minute_str] = time.split(":");
    let hour_i = parseInt(hour_str);
    let minute_i = parseInt(minute_str);
    if (valuep < 3) {
      hour_i = hour_i + 1;
      if (hour_i > 23) {
        hour_i = 0;
      }
    } else {
      minute_i = minute_i + 1;
      if (minute_i > 59) {
        minute_i = 0;
      }
    }
    setFormatTime(hour_i, minute_i);
  };

  const handleMin = () => {
    let [hour_str, minute_str] = time.split(":");
    let hour_i = parseInt(hour_str);
    let minute_i = parseInt(minute_str);
    if (valuep < 3) {
      hour_i = hour_i - 1;
      if (hour_i < 0) {
        hour_i = 23;
      }
    } else {
      minute_i = minute_i - 1;
      if (minute_i < 0) {
        minute_i = 59;
      }
    }
    setFormatTime(hour_i, minute_i);
  };

  const markCurrentPosition = (e) => {
    setValuep(e.target.selectionStart);
  }

  useEffect(() => {
    setFormatTime(hour, minute);
  }, [hour, minute]);

  return <>
    <input type="text" value={time} onChange={onChange} onClick={markCurrentPosition} onKeyUp={markCurrentPosition} />
    <div className="flex flex-col">
      <div className="hover:bg-gray-200" onClick={handleAdd}>
        <svg xmlns="http://www.w3.org/2000/svg" className="h-6 w-6 cursor-pointer" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={2}>
          <path strokeLinecap="round" strokeLinejoin="round" d="M5 15l7-7 7 7" />
        </svg>
      </div>
      <div className="hover:bg-gray-200" onClick={handleMin}>
        <svg xmlns="http://www.w3.org/2000/svg" className="h-6 w-6 cursor-pointer" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={2}>
          <path strokeLinecap="round" strokeLinejoin="round" d="M19 9l-7 7-7-7" />
        </svg>
      </div>
    </div>
  </>

}

export { TimeInput }

And used it in a parent component:

import { TimeInput } from "../components/TimeInput";

const Parent = () => {
  const [hour, setHour] = useState(0);
  const [minute, setMinute] = useState(0);
  const handleTimeInputChange = (e) => {
    console.log("handleTimeInputChange", e.target.value);
  };

 return <>
   <TimeInput hour={hour} minute={minute} onChange={handleTimeInputChange} />
</>
}

The time input arrow handle work and input display as expected. But onChange handle handleTimeInputChange was not invoked as expected. I want child componet time input pass data back to parent.

See demo here: https://codesandbox.io/s/bc15y3

What is a right react way to create a reusable packaged component?

0

2 Answers 2

0

In your App component, minute is not defined. When I remove minute variable, the code seems to work as normal. Next time try to provide a minimal producible example instead of posting everything. Not everyone is patient enough to dive into your code.

Sign up to request clarification or add additional context in comments.

3 Comments

Thanks for your reply. But the problem is not fixed. Input add/min handle worked, but onChange callback not trigged. I edit code to add missing code: const [minute, setMinute] = useState(0); This is forked code: codesandbox.io/s/bc15y3
The code works when I checked it. Are you using any plugin that might block script? When I click on the text and press a number, the event is triggered just fine, but if you use the arrow it will not call onChange.
Yes, directly type input will trigger onchange event, but input text is not changed.
0

It is because your component TimeInput is a Controlled Components , the value of the input tag in TimeInput is totally controlled by the Parent (here in your codesandbox is App).

Back to the onChange callback, here is handleTimeInputChange , we can find that it never change any state other than log the input value. To reach your goal, you're supposed to change the minute \ hour state value in handleTimeInputChange when onchange fired, like this:

//...
const handleTimeInputChange = (e) => {
    console.log("handleTimeInputChange", e.target.value);
    const [_hour, _minute] = e.target.value.split(":");
    setHour(parseInt(_hour));
    setMinute(parseInt(_minute));
  };

The above is just an example, the actual development needs to consider many boundary conditions.

3 Comments

I taked your advice, nothing changed. codesandbox.io/s/bc15y3
I guess you just pressed the delete key. In this scenario, the new minute value is still 0, try pressing any number key other than 0, open the console, and you will find logs.
Maybe question changed to: Why input onChange event not triggerd when change its value by code? stackoverflow.com/questions/42550341/…

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.