反应形式onChange-> setState落后一步

时间:2015-02-27 20:38:51

标签: javascript reactjs

我在构建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')
);

8 个答案:

答案 0 :(得分:41)

setState的调用并非同步。它会创建一个等待状态转换。&#34; (有关详细信息,请参阅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);
},

答案 1 :(得分:26)

有一种更简单的方法,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 的语法,请检查此link

答案 2 :(得分:4)

MyForm没有理由在这里使用州。同时将onChange放在表单而不是您感兴趣的输入上是奇怪的。受控制的组件应该是首选的,因为它们的行为更加明显,并且只要App的消息状态发生变化(即使您允许稍后更改消息),它在任何地方都是正确的。

这也使您的代码更短,更简单。

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

答案 3 :(得分:4)

因为这个原因,我把头发拉了一个小时,所以我决定分享一下...如果您的回叫仍落后一步,而且看似不起作用,请确保您不拨打电话打电话带有括号的函数...只需将其传递。菜鸟错误。

右:

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

VS

错误:

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

答案 4 :(得分:4)

带有setState钩子

useEffect(() => {
    your code...
}, [yourState]);

答案 5 :(得分:1)

您可以将基于类的组件重构为其他人提到的功能组件。缺点是这可能非常耗时,具体取决于您必须重构的代码行数,并且可能容易出错。

我将使用您的 changeHandler 示例来展示如何在功能组件中完成它。

const INITIAL_DATA = {
  message: ""
}
    
const [form, setForm] = useState({...INITIAL_DATA})

const changeHandler = (e) = setForm({
  ...form,
  [e.target.name]: e.target.value
})

<InputField name={message} value={form.message} onChange={changeHandler}>

^ 如您所解释的 onChange 落后于 setState 一步,上述代码将产生该行为。正如其他人所说,这是由于 setState 钩子的异步性质所致。

解决这个问题的方法是使用 useEffect 钩子,它允许你引入状态依赖。因此,当 setState 完成其更新周期时,useEffect 将触发。所以将下面的内容添加到上面的代码和 BAM 中..它应该显示状态变化而没有步进延迟。

useEffect(() => {
  doSomeValidationHere()
  orSendOffTheForm()
}, [form])

*注意我们如何在 useEffect(() => {}) 钩子之后添加依赖数组。

额外信息:

  • 如果依赖数组为空,它只会在组件挂载和第一次渲染时运行
  • 如果没有数组,每次页面渲染都会运行
  • 如果数组中有一个状态名称,它会在每次该状态设置完成时运行

答案 6 :(得分: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>
}
//...

答案 7 :(得分:0)

知道问题出在没有setState的异步行为后,我通过 async await

解决了我的问题
onChangeEmail=async x =>{
await this.setState({email:x})
}