Реагировать форму onChange-> setState на шаг назад

Я столкнулся с этой проблемой при создании webapp, и я воспроизвел его в jsfiddle. По сути, я хотел бы вводить вызов this.setState({message: input_val}) каждый раз, когда я ввожу в него что-то, а затем передаю его в родительский класс App, который затем повторно передает сообщение в класс Message. Однако вывод, кажется, всегда на один шаг ниже того, что я на самом деле печатаю. Демо-версия jsfiddle должна быть самоочевидной. Я задаюсь вопросом, не сделал ли я что-нибудь неправильно, чтобы предложить это.

HTML

<script src="http://facebook.github.io/react/js/jsfiddle-integration.js"></script>
<div id='app'></div>

js

var App = React.createClass({
    getInitialState: function() {
        return {message: ''}
    },
    appHandleSubmit: function(state) {
        this.setState({message: state.message});
        console.log(this.state.message);
    },
    render: function() {
        return (
            <div className='myApp'>
            <MyForm onChange={this.appHandleSubmit}/>
            <Message message={this.state.message}/>
            </div>
        );
    }
});

var MyForm = React.createClass({
    handleSubmit: function() {
        this.props.onChange(this.state);
    },
    handleChange: function(e) {
        console.log(e.target.value);
        this.setState({message: e.target.value});
        this.handleSubmit();
    },
    render: function() {
        return (
            <form className="reactForm" onChange={this.handleChange}>
            <input type='text' />
            </form>
        );
    }
});

var Message = React.createClass({
    render: function() {
        return (
            <div className="message">
                <p>{this.props.message}</p>
            </div>
        )
    }
});

React.render(
    <App />,
    document.getElementById('app')
);

Ответ 1

Вызов setState не является синхронным. Он создает "ожидающий переход состояния". (Подробнее см. здесь). Вы должны явно передать новое значение input как часть восходящего события (например, в handleSubmit в вашем примере).

См. этот пример.

handleSubmit: function(txt) {
    this.props.onChange(txt);
},
handleChange: function(e) {
    var value = e.target.value;
    this.setState({message: value});
    this.handleSubmit(value);
},

Ответ 2

Есть гораздо более простой способ сделать это, setState(updater, callback), setState(updater, callback) является асинхронной функцией, и она принимает обратный вызов в качестве второго аргумента,

Просто передайте handleSubmit в качестве обратного вызова методу setState. Таким образом, после завершения setState будет выполнен только handleSubmit.

Например,

handleChange: function(e) {
    console.log(e.target.value);
    this.setState({message: e.target.value}, this.handleSubmit);
}

Попробуйте изменить метод handleChange(), как описано выше, и он будет работать.

для синтаксиса использования setState проверьте эту ссылку

Ответ 3

Здесь нет никаких оснований для использования MyForm. Также помещать onChange в форму вместо ввода, который вас интересует, является нечетным. Контролируемые компоненты должны быть предпочтительнее, потому что их поведение является более очевидным, и любое изменение состояния сообщения приложения (даже если вы, например, разрешите Message изменять его позже), оно будет правильным везде.

Это также делает ваш код немного короче и значительно проще.

var App = React.createClass({
    getInitialState: function() {
        return {message: ''}
    },
    appHandleSubmit: function(message) {
        this.setState({message: message});
    },
    render: function() {
        return (
            <div className='myApp'>
                <MyForm onChange={this.appHandleSubmit} 
                        message={this.state.message} />
                <Message message={this.state.message}/>
            </div>
        );
    }
});

var MyForm = React.createClass({
    handleInputChange: function(e){
        this.props.onChange(e.target.value);
    },
    // now always in sync with the parent state
    render: function() {
        return (
            <form className="reactForm">
                <input type='text' onChange={this.handleInputChange}
                       value={this.props.message} />
            </form>
        );
    }
});

jsbin

Ответ 4

Из-за этого я тянул свои волосы в течение часа, поэтому я решил поделиться... Если ваш обратный вызов все еще на шаг позади и, казалось бы, не работает, убедитесь, что вы НЕ ВЫЗЫВАЕТЕ функцию с круглыми скобками... Просто передайте ее в. Ошибка новичка.

ПРАВО:

handleChange: function(e) {
    console.log(e.target.value);
    this.setState({message: e.target.value}, this.handleSubmit);
}

В.С.

НЕПРАВИЛЬНО:

handleChange: function(e) {
    console.log(e.target.value);
    this.setState({message: e.target.value}, this.handleSubmit());
}

Ответ 5

Мне было очень тяжело определить три функции обработчика, чтобы получить какое-то значение для состояния компонента, поэтому я решил не использовать состояние вообще. Я просто определил дополнительное свойство для компонента, который сохранил желаемое значение.

Итак, я закончил с кодом, который выглядел примерно так:

//...
},
text: '',
handleChange: function(event) {
  this.text = event.target.value;
  this.forceUpdate();
},
render: function() {
    return <div>
      <InputComponent onChange={this.handleChange}/>
      <DisplayComponent textToDisplay={this.text}/>
      </div>
}
//...

Ответ 6

Зная, что проблема заключается в отсутствии асинхронного поведения setState, я решил проблему с помощью асинхронного ожидания.

onChangeEmail=async x =>{
await this.setState({email:x})
}