简体   繁体   中英

Input onChange() event is not called

  • I'm rendering an Input of type='number' .

  • The Input has the value of this.state.value .

  • The Input and all the UI Components are generated via Semantic-UI , but I think that's not of a significant importance info.

  • I also have a custom arrow menu for this input instead of the original one. [input of type number has two arrows to decrease/increase the value]


Render()

render() {
    // Custom Menu
    const arrowsMenu = (
    <Menu compact size='tiny'>
      <Menu.Item as='a' onClick={ this.decreaseNumber.bind(this) }>
        <Icon name='chevron left' size='small' />
      </Menu.Item>
      <Menu.Item as='a' onClick={ this.increaseNumber.bind(this) }>
        <Icon name='chevron right' size='small' />
      </Menu.Item>
    </Menu>
    );

    return (
      <Input value={ this.state.value } type="number" label={ arrowsMenu } placeholder="Raplece ma" onChange={ this.onChange.bind(this) } />
      );
  }

The Custom Menu uses these two functions:

decreaseNumber(e) {
    this.setState({
      value: this.state.value - 1
    });
  }

  increaseNumber(e) {
    this.setState({
      value: this.state.value + 1
    });
  }

onChange

You can place anything.

onChange(e) {
    console.log('====================================');
    console.log('Hello pals');
    console.log('====================================');
  }

The problem is

That whenever I push an Arrow from the Menu , the onChange() event of the Input is not triggered . But the value of the input is changed .

(Of course, because the this.state.value variable is changed in the state)

If I do the same with the original arrows, of course, the value is changed as it should.

Why is that and how can I fix it?

onChange is only called if the user goes into the Input component and interacts with it to change the value (eg if they type in a new value). onChange is not called if you change the value programmatically through some other avenue (in your example changing it via the custom menu).

This is working as intended design.

If you want to trigger onChange, then call it from your increaseNumber and decreaseNumber methods.

You can call onChange with any code you want, but if you want to reflect the new value you need to set the state according to the new input value from the event.
As for decreaseNumber and increaseNumber you need to change the state as well but here you are doing calculation so you need to make sure it's a number (or convert it to a number) because you are getting a string back from the event.

Working example:

 class App extends React.Component { constructor(props) { super(props); this.state = { value: 0 }; } onChange = e => this.setState({value: e.target.value}); increaseNumber = () => this.setState({value: Number(this.state.value) + 1}); decreaseNumber = () => this.setState({ value: Number(this.state.value) - 1 }); render() { const { value } = this.state; return ( <div> <button onClick={this.decreaseNumber}>-</button> <input type="number" value={value} onChange={this.onChange}/> <button onClick={this.increaseNumber}>+</button> </div> ); } } ReactDOM.render(<App />, document.getElementById("root"));
 <script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script> <div id="root"></div>

Edit For triggering the onChange handler just call this.onChange but note that you can't pass the event like the native event handler does but you can pass a simple object that mimic the normal event object with a target.value .
Another option is to try triggering it via a ref but keep in mind it can cause an infinite loop in some cases. Edited Example:

 class App extends React.Component { constructor(props) { super(props); this.state = { value: 0 }; } onChange = (e) => { this.setState({ value: e.target.value }); console.log('change', e.target.value); } increaseNumber = () => { const { value } = this.state; const nextValue = Number(value) + 1; const changeEvent = { target: { value: nextValue } }; this.onChange(changeEvent); } decreaseNumber = () => { const { value } = this.state; const nextValue = Number(value) - 1; const changeEvent = { target: { value: nextValue } }; this.onChange(changeEvent); } render() { const { value } = this.state; return ( <div> <button onClick={this.decreaseNumber}>-</button> <input type="number" value={value} onChange={this.onChange} /> <button onClick={this.increaseNumber}>+</button> </div> ); } } ReactDOM.render(<App />, document.getElementById("root"));
 <script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script> <div id="root"></div>

Edit #2
As a followup to your comment:

list's value is its content/children. If some of the children change, then list changes with them as well

Well, this has an easy solution, you can use the ref (like i mentioned in the first section of my answer) and dispatch an event with bubbles:true so it will bubble all the way up to the parents.
Using your new example code:

 class App extends React.Component { liOnChange(e) { console.log('listed item/items changed'); } inputOnChange(e) { console.log('input changed'); } handleClick(e){ var event = new Event("input", { bubbles: true }); this.myInput.dispatchEvent(event); } render() { return( <div> <ul> <li onChange={this.liOnChange.bind(this)}> <input ref={ref => this.myInput = ref} type='text'onChange={this.inputOnChange.bind(this)}/> <button onClick={this.handleClick.bind(this)}>+</button> </li> </ul> </div> ); } } ReactDOM.render(<App />, document.getElementById("root"));
 <script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script> <div id="root"></div>

I in general don't like using refs but sometimes you need them.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM