【问题标题】:Handle real `change` event in React在 React 中处理真正的 `change` 事件
【发布时间】:2022-01-29 19:46:23
【问题描述】:

在 javascript 中,input 具有 change 事件,当输入失去焦点或用户使用升压或降压功能时发生type="number" 事件,但不是在正常输入时发生。

document.querySelector("input").addEventListener("change", e => {
  console.log(e.type, e.target.value)
})
<input type="number" step="0.01"> <button> </button>

但是当我使用 react 时,它会将 change 事件替换为 input 事件,该事件也发生在打字时:

function App() {
  const [val, setVal] = React.useState("")
  
  function onChange(e) {
    console.log(e.type, e.nativeEvent.type, e.target.value)
    setVal(e.target.value)
  }
  
  return <input type="number" step="0.01" value={val} onChange={onChange} />
}

ReactDOM.render(<App />, document.getElementById("app"))
<script crossorigin src="//unpkg.com/react@17/umd/react.production.min.js"></script>
<script crossorigin src="//unpkg.com/react-dom@17/umd/react-dom.production.min.js"></script>
<span id="app"></span> <button>&nbsp;</button>

我需要处理打字时不会发生的正常change 事件。
我该怎么做?

PS:片段中的按钮是一个可以tabinput跳转到的地方。

是的,“当键入时会导致编辑值出现问题”部分确实有点帮助,尽管这对我来说似乎是代码异味,可能是 XY 问题。您能否分享在以正常 React 方式使用 onChange 时,输入输入是如何导致问题的?

我不想完全拒绝使用它,我只想同时使用它。

这是一个有问题的例子:

  • 您不能在小数部分使用退格(除非您输入了超过 2 个十进制数字)
  • 如果在小数部分使用delete,则加零,光标跳到末尾
  • 如果当前值为数字,例如1.&lt;cursor&gt;20,并且您输入数字,光标将跳到末尾
  • 如果当前值类似于&lt;cursor&gt;1000.00,然后按删除,数字变为0.00
  • 如果您尝试在空输入中写入,则在按下第一个数字后会变成7.00&lt;cursor&gt;,并且很难输入类似 7.23 的内容
  • 我很难从输入中删除所有值,因为它变成了0.00,直到你全部选择它
  • 我认为,还有更多问题

function App() {
  const [val, setVal] = React.useState("")
  
  function onChange(e) {
    console.log(e.type, e.nativeEvent.type, e.target.value)

    setVal(e.target.value == +e.target.valueAsNumber.toFixed(2)
      ? e.target.valueAsNumber.toFixed(2)
      : e.target.value
    )
  }
  
  return <input type="number" step="0.01" value={val} onChange={onChange} />
}

ReactDOM.render(<App />, document.getElementById("app"))
<script crossorigin src="//unpkg.com/react@17/umd/react.production.min.js"></script>
<script crossorigin src="//unpkg.com/react-dom@17/umd/react-dom.production.min.js"></script>
<span id="app"></span> <button>&nbsp;</button>

如果我尝试使用onBlur fpr 格式,它会变得更好,但是:

  • 当您单击 step upstep down 按钮或按 updown 时,数字未格式化在具有相同效果的键盘上:

function App() {
  const [val, setVal] = React.useState("")
  
  function onChange(e) {
    console.log(e.type, e.nativeEvent.type, e.target.value)
    setVal(e.target.value)
  }
  
  function onBlur(e) {
    console.log(e.type, e.nativeEvent.type, e.target.value)
    if (e.target.value && e.target.value == +e.target.valueAsNumber.toFixed(2)) {
      setVal(e.target.valueAsNumber.toFixed(2))
    }
  }
  
  return <input type="number" step="0.01" value={val} onChange={onChange} onBlur={onBlur} />
}

ReactDOM.render(<App />, document.getElementById("app"))
<script crossorigin src="//unpkg.com/react@17/umd/react.production.min.js"></script>
<script crossorigin src="//unpkg.com/react-dom@17/umd/react-dom.production.min.js"></script>
<span id="app"></span> <button>&nbsp;</button>

所以我想使用出现在模糊和台阶上的change

【问题讨论】:

  • onChange 在 React 中的工作方式与在 vanilla JS 中的工作方式不同。您可以使用 onBlur 事件并通过 React ref 获取输入的值。有没有令人信服的理由不只使用onChange 处理程序?你可能会发现这个post 很有帮助。
  • @DrewReese, onBlur 不会出现在上台阶下。 FF eve 允许在不从另一个控件移动焦点的情况下单击它们。
  • 诚然,我 99.9% 的 JS 经验都在 React 中,所以 React 的 onChange 是“正常”行为。也许如果你描述了你想要完成的事情,或者明确地描述了你想要的行为,那么你想要在 React 中做什么可能会更清楚。仅使用 onChange 处理程序是否存在问题/问题?
  • @DrewReese,似乎我已经描述得足够准确:函数应该在模糊或向上/向下时调用,而不是在打字时调用。如果你想知道这个功能,那就是:if (e.target.value &amp;&amp; !isNaN(e.target.valueAsNumber)) e.target.value = e.target.valueAsNumber .toFixed(2)。键入时不需要调用它,因为它会导致编辑值出现问题。对你有帮助吗?
  • 是的,“当键入时会导致编辑值出现问题”部分确实有点帮助,尽管这对我来说似乎是代码异味,可能是 XY 问题。您能否分享在以正常 React 方式使用 onChange 时输入输入会导致问题的原因?

标签: javascript reactjs dom-events onchange


【解决方案1】:

这里有一个拦截所有感兴趣的输入类型的方法,链接到codesandbox -> https://codesandbox.io/s/summer-resonance-58n15?file=/src/App.js:

function App() {
  const [val, setVal] = React.useState("");

  function onChange(e) {
    if (e.nativeEvent.inputType === "insertText") {
      console.log("input");
      // Warning: Using undefined would also comprehend other events
      // that may be not specified right now in the documentation, 
      // but could be emitted, hence causing bugs.
      // Maybe a possible future improvement could be to specify
      // this particular inputType.
    } else if (e.nativeEvent.inputType === undefined) {
      if (e.target.value > val) {
        console.log("step up");
      } else {
        console.log("step down");
      }
    } else if (e.nativeEvent.inputType === "deleteContent") {
      console.log("deleting");
    }
    setVal(e.target.value);
  }

  return (
    <React.Fragment>
      <input type="number" step="0.1" value={val} onChange={onChange} />{" "}
      <button>&nbsp;</button>
    </React.Fragment>
  );
}

【讨论】:

  • 非常有趣的方式。
  • the spec“其他规范可能会扩展此定义”时,不确定是否可以依赖inputType === undefined
  • 是的,这个特定的 inputType 类型不存在有点好笑。当然不行,这是一个 hacky 解决方案,我正要在 ``` undefined ``` 行写评论,但 StackOverflow 进入了维护模式。使用 undefined 还可以理解文档中现在可能未指定但可能会发出的其他事件,从而导致错误。但是很好,也许我们发现了规范的未来可能的改进。
【解决方案2】:

您可以像在 Javascript 中一样简单地创建输入的 ref 并创建 eventListener:

import React from "react";
import { render } from "react-dom";

const App = () => {
  const inputRef = React.useRef();

  React.useEffect(() => {
    inputRef.current.addEventListener('change', (e) => console.log(e.target.value))
  })

  return (
    <div>
      <input ref={inputRef} type="number" />
    </div>
  );
};

render(<App />, document.getElementById("root"));

【讨论】:

  • 在卸载组件时,您可能还应该删除事件侦听器。
  • 监听器应该被移除并且不应该在每次渲染时更新。关于这个想法,是的,这是可能的,但我希望有更好的方法。
  • @Qwertiy 好吧,我根据经验知道,像 antonio 在其回答中所做的那样管理事件可能会导致奇怪的行为,总是有一些我们没有考虑到的参数,比如提供不同的键盘许多。我仍然更喜欢已经存在且安全的解决方案,而不是我无法完全控制的解决方案。现在,Antonio 的回答效果很好,应该考虑一下,它只取决于您的应用的目的是什么。
猜你喜欢
  • 2017-03-14
  • 1970-01-01
  • 2021-12-24
  • 1970-01-01
  • 2020-10-23
  • 2014-09-05
  • 1970-01-01
  • 1970-01-01
  • 2014-08-28
相关资源
最近更新 更多