JavaScript React 中 input 元素的 onChange 事件怎么没有执行?

u1435638317 · May 25, 2022 · Last by zzz6519003 replied at May 28, 2022 · 781 hits

我使用 react 封装了一下如下的时间调整的输入控件

控件的代码如下,就是给一个 input 元素加了两个调整控件,可以用来调整输入的小时和分钟。

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 }

然后在父元素中使用这个 TimeInput 组件:

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} />
</>
}

测试发现,控件的时间可以自由调整,问题是它的 onChange 事件回调 handleTimeInputChange 没有执行。我需要在这个 onChange 回调事件时获取控件输入的值。

谁能解释一下是什么原因吗?我感觉是我对 Reactjs 的数据流理解的不到位造成的。

绑定 value 不会触发 input 的 onChange 事件。

直接在这里抛出。

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

// 父元素
const handleTimeInputChange = (time) => {
  console.log(`current_time: ${time}`);
};
Reply to heroyct

你这个好像不是正确的解决问题的方法。找了一大圈,发现在 react 中,通过代码的方法修改 input 的 value 不会触发 input 的 onChange 事件,要自己分发事件才行??只有手动输入才会触发。但是我找不到相关的文档。

找了一大圈,发现在 react 中,通过代码的方法修改 input 的 value 不会触发 input 的 onChange 事件,要自己分发事件才行??只有手动输入才会触发。

是的,和你说的一样。stackoverflow 有类似问题,可以参考下。

可以使用 useEffect

useEffect(() => {
  onChange(time);
}, [time]);

从这个事情说明,React 真是不简单,简单的表面,内部有着复杂的实现。之前还遇到一个 React 的问题就是,input 输入框无法输入,然后找到了 Controlled component 这个名词。真是为了解决一个问题,又引入了另一个问题。

原生 dom 用 js 更改 input 的值是不会触发事件的,要手动触发,可能是遵循了原生的惯例。

Reply to u1435638317

仁者见仁智者见智,把复杂度留给用户自行选择和内部抽象好再吐出来是个设计权衡。不过 React 写起来还是挺罗嗦的,使用体验比较差,欣赏不太了它的设计哲学。

Reply to u1435638317

Controlled component 这个概念不了解也不影响的 算不上 react 的锅

Reply to Rei

可以加一个前端的节点啦 XD

You need to Sign in before reply, if you don't have an account, please Sign up first.