【问题标题】:Hidden input doesn't trigger React's onChange event隐藏的输入不会触发 React 的 onChange 事件
【发布时间】:2019-09-15 10:27:17
【问题描述】:

我在 React 的输入字段中遇到了一个奇怪的问题。我知道hidden 输入不会触发inputchange 事件。但是,即使我手动触发它们,React 的 onChange 事件仍然没有被调用。

我在这里同时触发changeinput 事件,因为React 的onChange 实际上是input 事件。当我出于测试目的在inputRef (addEventListener("change", () => { ... })) 上设置事件侦听器时,调用它时不会出现问题。然而,事实证明 React 在拦截它时遇到了一些问题。

这是我当前的代码:

const [fieldValue, setFieldValue] = useState(0);
const inputRef = useRef<HTMLInputElement>(null);

const handleClick = useCallback((): void => {
    if (inputRef.current) {
        inputRef.current.dispatchEvent(new Event("change", { bubbles: true }));
        inputRef.current.dispatchEvent(new Event("input", { bubbles: true }));
    }

    setFieldValue(prev => prev + 1);
}, []);

JSX:

<input type="hidden" ref={inputRef} value={fieldValue} onChange={(e) => { console.log("React:onChange"); }} />
<button type="button" onClick={handleClick}>Hit it</button>

我在这里做错了吗?我还需要做什么才能正确触发 React 的 onChange 事件?

【问题讨论】:

  • 如果能放入codesandbox就好了
  • 为什么需要ref?看起来你的fieldValue处于状态,你应该在onChange中设置新状态,点击按钮时只需获取当前状态值,它将保持当前输入值
  • @KryštofŘeháček 这些有什么关系?我触发更改,而不是寻找解决方案如何做到这一点。
  • @quirimmo 我会将ref 用于其他目的,例如 HTML5 验证等,因此我需要访问 DOM 对象。

标签: javascript reactjs typescript dom-events


【解决方案1】:

React 中的事件由 react-dom 通过不同的插件处理。输入事件由 SimpleEventPlugin 管理(请参阅https://github.com/facebook/react/blob/master/packages/react-dom/src/events/SimpleEventPlugin.js

此插件获取事件并重新调度它而不会过多干扰。因此,您可以将它作为原生事件进行调度,它会被触发为 SyntheticEvent 而无需进行太多更改。

Change 事件由 ChangeEventPlugin 处理(参见https://github.com/facebook/react/blob/master/packages/react-dom/src/events/ChangeEventPlugin.js)。这个插件有这个目的:

这个插件创建一个 onChange 事件来规范化更改事件 跨表单元素。此事件在可以触发的时候触发 更改元素的值而不会看到闪烁。

插件触发更改事件,但不完全与本机更改事件一样。例如,文本输入元素上的本机更改事件仅在模糊时触发。见https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/change_event

对于某些元素,包括 ,更改事件 在控件失去焦点之前不会触发。

但是对于 React,它的触发方式不同,每次更改值时都会触发。为此,它由具有以下限制的插件处理: 该事件将仅在以下输入类型上触发:

  color: true,
  date: true,
  datetime: true,
  'datetime-local': true,
  email: true,
  month: true,
  number: true,
  password: true,
  range: true,
  search: true,
  tel: true,
  text: true,
  time: true,
  url: true,
  week: true

所以在隐藏输入上,react 会停止调度。

另一个限制是只有当输入的值实际发生变化时才会调度事件。见

 if (updateValueIfChanged(targetNode)) {
    return targetInst;
  }

因此,即使在受支持的输入上,您的调度也不会通过插件。您可以在此 sn-p 中看到它,通过操作用于获取输入值的方法,您可以设法调度事件。

class Hello extends React.Component {
  constructor(props) {
    super(props);
    this.myRef = React.createRef();

  }

  componentDidMount() {
  //_valueTracker.getValue is used for comparing the values of the input. If you write fake value in the input it'll dispatch, anything else won't
    document.getElementsByTagName('INPUT')[0]._valueTracker.getValue = () => {
      return 'fake value'
    }
  }

  render() {

    const handleClick = () => {
      if (this.myRef) {

        this.myRef.current.dispatchEvent(new Event("change", {
          bubbles: true
        }));


      }
    };

    return <div > < input type = "text"
    ref = {
      this.myRef
    }
    onChange = {
      (e) => {
        console.log("React:onChange");
      }
    }
    /><button type="button"  onClick={handleClick}>Hit it</button > < /div>

  }
}

ReactDOM.render( <
  Hello name = "World" / > ,
  document.getElementById('container')
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>

<div id="container">

</div>

所以对于隐藏的输入,没有办法让它工作(注意它是符合标准的)。对于文本(或其他支持的输入),您可以,但您需要或多或少地破解比较事件之前的值与事件之后的值的方法。

【讨论】:

  • 这是我一直在争取的答案。感谢您的详细解释。
【解决方案2】:

onChange 事件被触发 when the user alters 元素值 - 在您的情况下,您正在以编程方式更改输入值,因此 onChange 没有触发

onInputevents fires 当值改变时——不管怎样。因此你需要添加onInput甚至监听器而不是onChange

<input type="hidden" ref={inputRef} value={fieldValue} onInput={(e) => { console.log("React:onChange"); }} />

这是一个工作示例:Sandbox

【讨论】:

  • in your case, you are programmatically changing the input value and hence onChange is not firing up 正确,但不要忘记我也在手动触发change 事件。那么应该可以正常工作。如果我的代码中没有手动触发器,onInput 也不会工作。很奇怪,因为 React onChange 是一个实际的 input 事件 (see this answer)。
  • 如果您的输入被隐藏,则您的onChange 不会被触发,因为正如我链接的文档中所指定的那样,onchange 事件处理程序仅在用户更改它时才会触发。如果它被隐藏了,没人能改变它吗?
  • 我认为这不是真的。将输入类型更改为 text 并且 React 的 onChange 也将永远不会被调用。此外,正如我所说,手动触发器在 Javascript 中工作:只需将事件侦听器添加到 inputRef.current 以验证它是否有效并在手动触发时被调用。那么问题一定是 React 特有的。
【解决方案3】:

我认为您可能以错误的方式处理此问题。当您单击按钮时,您希望状态更新并且新状态反映在隐藏字段中。而不是检查隐藏字段中的更改,您应该检查状态的更改,您可以使用useEffect 轻松完成。这样你就可以取消你的 refs 而不会让事情变得过于复杂。

function App() {
  const [fieldValue, setFieldValue] = useState(0);

  function handleClick() {
    setFieldValue(prev => prev + 1);
  }

  useEffect(() => {
     console.log('React:changed');
  }, [fieldValue]);

  return (
    <>
      <input type="hidden" value={fieldValue} />
      <button type="button" onClick={handleClick}>
        Hit it
      </button>
    </>
  );
}

Sandbox

【讨论】:

    猜你喜欢
    • 2011-10-24
    • 2021-06-10
    • 2021-05-10
    • 1970-01-01
    • 1970-01-01
    • 2012-06-12
    • 1970-01-01
    • 1970-01-01
    • 2020-11-22
    相关资源
    最近更新 更多