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

u1435638317 · 2022年05月25日 · 最后由 zzz6519003 回复于 2022年05月28日 · 767 次阅读

我使用 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}`);
};
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 的值是不会触发事件的,要手动触发,可能是遵循了原生的惯例。

u1435638317 回复

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

u1435638317 回复

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

Rei 回复

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

需要 登录 后方可回复, 如果你还没有账号请 注册新账号