react表单 onChange->setState 落后一步

IT技术 javascript reactjs
2021-03-23 08:18:05

我在构建 webapp 时遇到了这个问题,我在这个jsfiddle 中复制了它本质上,我希望this.setState({message: input_val})每次输入内容时都会调用一个输入,然后将其传递给父 App 类,然后父 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')
);
6个回答

调用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);
},

有一个更简单的方法来做到这一点,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检查此链接的语法

+1 这是迄今为止我认为最明智的答案。您也可以使用 componentDidUpdate 提到替代方案;)
2021-06-06 08:18:05

由于这个原因,我拉了一个小时的头发,所以我决定分享......如果你的回调仍然落后一步并且似乎不起作用,请确保你不要用括号调用该函数......只需传递它in.菜鸟错误。

对:

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());
}
或者当然,您可以这样做,this.setState({message: e.target.value}, () => this.handleSubmit());或者handleSubmit.bind(...)如果您必须将不同的参数传递给 handleSubmit。
2021-06-06 08:18:05

带有 setState 钩子

useEffect(() => {
    your code...
}, [yourState]);
useEffect 是功能组合的生命周期管理钩子,而不是旧类。如果你曾经使用过钩子,这些文档是非常直接的。如果不学起来。基本上,只要变量/参数“yourState”发生变化,代码就会运行,而不是在它更新之前(让你落后一步)
2021-06-05 08:18:05
你能扩大这个答案吗?
2021-06-08 08:18:05

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

感谢您的重构,我研究了您的代码并从中吸取了一些好处。似乎对纯视图使用 this.props 是一个好主意,而使用 this.state 对控制器很有用。
2021-05-25 08:18:05
仅供参考 - 此代码无法按原样工作。onChange不会像您在那里所做的那样将输入框的实际文本作为值传输(SyntheticEvent实际上是通过了 a)。我也不期望容器应该知道有关如何呈现子组件的细节的任何信息,因此它不应该e.target.value用于例如从输入框中提取值。这就是为什么MyForm在原始代码中具有一定程度的间接性非常有用的原因
2021-05-30 08:18:05
谢谢,修复并添加了一个演示。
2021-06-02 08:18:05
大多数情况下,是的。偶尔会有类似下拉菜单的东西,其中打开/关闭状态可以对组件保持私有(你为什么要关心下拉菜单是否在父级中打开?)。此外,有时 UI 组件需要跟踪诸如光标位置之类的内容并在渲染中使用它,以便最终处于其本地状态。
2021-06-17 08:18:05