简体   繁体   中英

Input onChange and React logic

I'm a javascript lover but I'm a fresh React beginner. I've got some trouble understanding the React logic so I've made a simple example, an app with a numeric input and a cancel button that cancels last action:

 class App extends React.Component { //A simple numeric input and a cancel button constructor(props){ super(props); this.state={ selectedNumber:0, lastNumber:null } } render() { return ( <div className="App"> <input type="number" value={this.state.selectedNumber} onChange={this.onNumberChange}></input> <button disabled={this.state.lastNumber===null} onClick={this.onUndo}>Undo last action</button> </div> ); } onNumberChange=(e)=>{ let prevNumber=this.state.selectedNumber; this.setState({selectedNumber:e.target.value, lastNumber:prevNumber}); } onUndo=(e)=>{ let prevNumber=this.state.lastNumber; this.setState({selectedNumber:prevNumber,lastNumber:null}); } } ReactDOM.render(<App/>, document.getElementById('root')); 
 <html> <head> <meta charset="utf-8"> </head> <body> <div id="root"> </div> <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> </body> </html> 

Everything works fine but let's say the user enters the number 200 (by typing), and then want to undo this value and retrieve the last one, it will fallback to 20 because each keyboard entry refresh the state with the input value...

To avoid this behaviour I tried to move the input into a custom component, add local state to it and renders the input value according to the component props. I added a onChange event locally that set the component state accordingly to user "live" typing, and I binded a onBlur event to the parent's callback that sets its own state accordingly to "complete" user entry.

The component renders well initially, renders well when you type in it, but doesn't update when changed externally by the cancel button...

 class App extends React.Component { //A simple numeric input and a cancel button constructor(props){ super(props); this.state={ selectedNumber:0, lastNumber:null } } render() { return ( <div className="App"> <MyNumericInput selectedNumber={this.state.selectedNumber} onBlur={this.onBlur}/> <button disabled={this.state.lastNumber===null} onClick={this.onUndo}>Undo last action</button> </div> ); } onBlur=(e)=>{ let prevNumber=this.state.selectedNumber; this.setState({selectedNumber:e.target.value, lastNumber:prevNumber}); } onUndo=(e)=>{ let prevNumber=this.state.lastNumber; this.setState({selectedNumber:prevNumber,lastNumber:null}); } } class MyNumericInput extends React.Component { constructor(props){ super(props); this.state={ selectedNumber:props.selectedNumber } } render() { return( <input type="number" value={this.state.selectedNumber} onChange={this.onChange} onBlur={this.props.onBlur}/>); } onChange=(e)=>{ this.setState({selectedNumber:e.target.value}); } } ReactDOM.render(<App/>, document.getElementById('root')); 
 <html> <head> <meta charset="utf-8"> </head> <body> <div id="root"> </div> <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> </body> </html> 

I think you can do this.

onBlur = () => {
  this.setState(prevState => ({ lastNumber: prevState.selectedNumber }));
}

onChange = (e) => {
  this.setState({ selectedNumber: e.target.value });
}

Now, everytime the focus in blurred from the input, your lastNumber is updated. And while clicking undo button it should work fine. I hope it works. Happy coding :)

I've found a solution on this very forum...

stackoverflow.com/questions/53479598/reactjs-save-input-value-on-blur-and-not-on-every-key-stroke

BUT React documentation tells that it's not a good practice...

You may call setState() immediately in componentDidUpdate() but [...] it would also cause an extra re-rendering which, while not visible to the user, can affect the component performance. If you're trying to “mirror” some state to a prop coming from above, consider using the prop directly instead.

I put my updated code with the solution given on the link above, can you confirm that this approach is not good and what is the alternative?

 class App extends React.Component { //A simple numeric input and a cancel button constructor(props){ super(props); this.state={ selectedNumber:0, lastNumber:null } } render() { return ( <div className="App"> <MyNumericInput selectedNumber={this.state.selectedNumber} onBlur={this.onBlur}/> <button disabled={this.state.lastNumber===null} onClick={this.onUndo}>Undo last action</button> </div> ); } onBlur=(e)=>{ let prevNumber=this.state.selectedNumber; this.setState({selectedNumber:e.target.value, lastNumber:prevNumber}); } onUndo=(e)=>{ let prevNumber=this.state.lastNumber; this.setState({selectedNumber:prevNumber,lastNumber:null}); } } class MyNumericInput extends React.Component { constructor(props){ super(props); this.state={ selectedNumber:props.selectedNumber } } render() { return( <input type="number" value={this.state.selectedNumber} onChange={this.onChange} onBlur={this.props.onBlur}/>); } onChange=(e)=>{ this.setState({selectedNumber:e.target.value}); } componentDidUpdate(prevProps) { if (this.props.selectedNumber !== prevProps.selectedNumber && this.props.selectedNumber !== this.state.selectedNumber) { this.setState({selectedNumber: this.props.selectedNumber}); } } } ReactDOM.render(<App/>, document.getElementById('root')); 
 <html> <head> <meta charset="utf-8"> </head> <body> <div id="root"> </div> <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> </body> </html> 

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