Я любитель javascript, но я новичок в React. У меня возникли проблемы с пониманием логики React, поэтому я сделал простой пример, приложение с числовым вводом и кнопкой отмены, которая отменяет последнее действие:
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>Все работает нормально, но скажем, пользователь вводит число 200 (набрав), а затем хочет отменить это значение и получить последнее, оно вернется к 20, потому что каждая запись на клавиатуре обновляет состояние с введенным значением...
Чтобы избежать такого поведения, я попытался переместить ввод в пользовательский компонент, добавить к нему локальное состояние и отобразить входное значение в соответствии с реквизитами компонента. Я добавил событие onChange локально, которое устанавливало состояние компонента в соответствии с «живым» вводом пользователя, и я привязывал событие onBlur к обратному вызову родителя, которое устанавливает свое собственное состояние в соответствии с «завершенной» записью пользователя.
Компонент хорошо отображается изначально, хорошо отображается при вводе, но не обновляется при внешнем изменении с помощью кнопки отмены...
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>


![Безумие обратных вызовов в javascript [JS]](https://i.imgur.com/WsjO6zJb.png)


Я думаю, вы можете это сделать.
onBlur = () => {
this.setState(prevState => ({ lastNumber: prevState.selectedNumber }));
}
onChange = (e) => {
this.setState({ selectedNumber: e.target.value });
}
Теперь каждый раз, когда фокус размывается при вводе, ваш lastNumber обновляется. И при нажатии кнопки отмены все должно работать нормально. Надеюсь это работает. Удачного кодирования :)
Я нашел решение на этом же форуме...
НО документация React говорит, что это не очень хорошая практика...
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.
Я поставил свой обновленный код с решением, приведенным по ссылке выше, можете ли вы подтвердить, что этот подход не годится и какова альтернатива?
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>
Я попробовал ваш код, но кнопка отмены больше не работает... Ps: я отредактировал приведенный выше фрагмент, так что теперь его можно проверить