2015-02-27 2 views
18

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

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') 
); 

ответ

24

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

См. this пример.

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

, сэр заслуживают вашего 41K репутацию! –

4

У MyForm нет оснований. Также помещать onChange в форму вместо ввода, который вас интересует, является нечетным. Контролируемые компоненты должны быть предпочтительнее, потому что их поведение более очевидно, и в любое время, когда состояние сообщения App изменяется (даже если вы, например, разрешите 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's state 
    render: function() { 
     return (
      <form className="reactForm"> 
       <input type='text' onChange={this.handleInputChange} 
         value={this.props.message} /> 
      </form> 
     ); 
    } 
}); 

jsbin

+0

Спасибо за рефакторинг, я изучил ваш код и взял некоторые хорошие моменты от него. Похоже, что использование this.props - хорошая идея для чистых просмотров, и использование this.state подходит для контроллеров. –

+0

В основном, да.Иногда есть что-то вроде раскрывающегося списка, в котором открытое/закрытое состояние может быть закрыто для компонента (почему бы вам вообще не заботиться, открыт ли раскрывающийся список в родительском?). Также иногда компоненты пользовательского интерфейса должны отслеживать что-то вроде положения курсора и использовать его в рендере, чтобы оно попадало в локальное состояние. – FakeRainBrigand

+0

FYI - этот код не работает как есть. 'onChange' не передает фактический текст поля ввода в качестве значения, как вы там делали (фактически« SyntheticEvent »). Я также не ожидал, что контейнер должен знать что-либо о специфике представления дочернего компонента, поэтому он не должен использовать «e.target.value», например, для извлечения значения из поля ввода. Вот почему наличие уровня косвенности очень полезно в чем-то вроде «MyForm» в исходном коде. – WiredPrairie

0

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

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

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

Там гораздо более простой способ сделать это,

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

Для примера.

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

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

синтаксис использования SetState проверить это link

+0

+1 Это, безусловно, самый разумный ответ, который я думаю. Вы могли бы упомянуть об альтернативе с компонентом componentDidUpdate;) – Kjellski

Смежные вопросы