最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - Hidden input doesn't trigger React's onChange event - Stack Overflow

programmeradmin6浏览0评论

I'm facing a weird issue with the input field in React. I'm aware of the fact that hidden input do not trigger input nor change events. However, even if I trigger them manually, React's onChange event is still not being invoked.

I trigger both, change and input event here because React's onChange is in fact the input event. When I setup an event listener on the inputRef (addEventListener("change", () => { ... })) for testing purposes, it's being called without without problems. However, it turns out React is having some issue intercepting it.

Here is my current code:

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>

Am I doing anything wrong here? What else do I have to do to properly trigger React's onChange event?

I'm facing a weird issue with the input field in React. I'm aware of the fact that hidden input do not trigger input nor change events. However, even if I trigger them manually, React's onChange event is still not being invoked.

I trigger both, change and input event here because React's onChange is in fact the input event. When I setup an event listener on the inputRef (addEventListener("change", () => { ... })) for testing purposes, it's being called without without problems. However, it turns out React is having some issue intercepting it.

Here is my current code:

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>

Am I doing anything wrong here? What else do I have to do to properly trigger React's onChange event?

Share Improve this question asked Sep 15, 2019 at 10:27 RA.RA. 1,0291 gold badge14 silver badges38 bronze badges 7
  • would be nice if you can put it in codesandbox – Matthew Barbara Commented Sep 15, 2019 at 10:31
  • Possible duplicate of jQuery - Detect value change on hidden input field – Kryštof Řeháček Commented Sep 15, 2019 at 10:31
  • Why do you need the ref? It looks like your fieldValueis in the state, you should set the new state in the onChange and when clicking in the button simply get the current state value, it will hold the current input value – quirimmo Commented Sep 15, 2019 at 10:43
  • @KryštofŘeháček How does these relate? I trigger changes, not looking for solution how to do that. – RA. Commented Sep 15, 2019 at 10:46
  • @quirimmo I will be using ref for other purposes like the HTML5 validation and stuff like that hence I need to access the DOM object. – RA. Commented Sep 15, 2019 at 10:48
 |  Show 2 more ments

3 Answers 3

Reset to default 10 +50

Events in React are handled by react-dom, through different plugins. Input events are managed with the SimpleEventPlugin (see https://github./facebook/react/blob/master/packages/react-dom/src/events/SimpleEventPlugin.js)

This plugin gets an event and redispatches it without interfering too much. So you can dispatch it as a native event, and it'll be triggered as a SyntheticEvent without much changes.

Change event is handled by the ChangeEventPlugin (see https://github./facebook/react/blob/master/packages/react-dom/src/events/ChangeEventPlugin.js). This plugins has this purpose:

This plugin creates an onChange event that normalizes change events across form elements. This event fires at a time when it's possible to change the element's value without seeing a flicker.

The plugin triggers change event but not exactly as the native change event. The native change event on text input element for example is triggered only on blur. See https://developer.mozilla/en-US/docs/Web/API/HTMLElement/change_event

For some elements, including , the change event doesn't fire until the control loses focus.

But with React, it's triggered differently, on every change of value. To do this, it is handled by the plugin which has these restrictions: The event will be triggered only on these input types:

  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

So on hidden inputs, react stops the dispatch.

Another restriction is that the event will be dispatched only if the value of the input actually changes. See

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

So even on a supported input, your dispatch won't go through the plugin. You can see it in this snippet, by manipulating a method that is used to get the value of the input you can manage to dispatch the event.

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

  }

  ponentDidMount() {
  //_valueTracker.getValue is used for paring 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./ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare./ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>

<div id="container">

</div>

So for a hidden input, there's no way to make it work (note that it is in line with the standard). For a text (or other supported inputs), you can but you need to more or less hack the method paring the value before the event with the value after the event.

The onChange event is fired when the user alters the element value - in your case, you are programmatically changing the input value and hence onChange is not firing up

onInput events fires when value changes - regardless how. Therefore you need to add onInput even listener instead of onChange.

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

Here is a working example: Sandbox

I think you might be approaching this the wrong way. When you click the button you want the state to update and that new state reflected in the hidden field. Instead of checking the change in the hidden field you should be checking the change in the state which you can do very easily with useEffect. That way you can do away with your refs and not over-plicate things.

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

发布评论

评论列表(0)

  1. 暂无评论