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() {
// 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
});
}
You can place anything.
onChange(e) {
console.log('====================================');
console.log('Hello pals');
console.log('====================================');
}
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.