React方法中的多个setState()调用:如何使其“同步”工作

时间:2016-12-07 00:19:07

标签: javascript reactjs asynchronous material-ui setstate

所以我在我的React应用程序中遇到了问题,我遇到了一个特定情况,我需要在一个方法中进行多次setState()调用,然后让代码运行 AFTER 状态被设定了。下面的代码是一个用于在网站上添加帐户的对话框。

import React from 'react';
import Dialog from 'material-ui/Dialog';
import FlatButton from 'material-ui/FlatButton';
import TextField from 'material-ui/TextField';

/**
 * A modal dialog can only be closed by selecting one of the actions.
 */
export default class NewAcctDia extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      open: false,
      userError: null,
      passError: null,
      passConfirmError: null,
    }

    this.handleOpen = this.handleOpen.bind(this);
    this.handleClose = this.handleClose.bind(this);
    this.handleSubmit = this.handleSubmit.bind(this);
  }

  handleOpen() {
    this.setState({open: true});
  }

  handleClose() {
    this.setState({open: false});
  }

  handleSubmit() {
    if(!this.refs.user.getValue())
      this.setState({userError: "This field is required"});
    else
      this.setState({userError: null});

    if(!this.refs.pass.getValue())
      this.setState({passError: "This field is required"});
    else
      this.setState({passError: null});

    if(this.refs.pass.getValue() == this.refs.passConfirm.getValue()) {
      this.setState({passError: null});
    } else {
      this.setState({passConfirmError: "Passwords do not match"});
    }

    if(!this.state.userError && !this.state.passError && !this.state.passConfirmError)
      alert('worked');
  }

  render() {
    const actions = [
      <FlatButton
        label="Cancel"
        primary={true}
        onTouchTap={this.handleClose}
      />,
      <FlatButton
        label="Submit"
        primary={true}
        disabled={false}
        onTouchTap={this.handleSubmit}
      />,
    ];

    return (
        <Dialog
          title="Create an Account"
          actions={actions}
          modal={true}
          open={this.state.open}
          contentStyle={{width: 350}}
        >
          <TextField
            ref='user'
            floatingLabelText="Username"
            errorText={this.state.userError}
          /><br />
          <TextField
            ref='pass'
            floatingLabelText="Password"
            type="password"
            errorText={this.state.passError}
          /><br />
          <TextField
            ref='passConfirm'
            floatingLabelText="Confirm Password"
            type="password"
            errorText={this.state.passConfirmError}
          /><br />
        </Dialog>
    );
  }
}

问题在于handleSubmit()方法,我需要检查用户是否在用户名和密码字段中输入了内容,并且密码和确认密码字段匹配。如果他们没有,我会将错误文本添加到需要通过状态进行更改的字段中。然后我试着查看状态,看看是否有任何错误。

不幸的是,正如我很快发现的那样,setState()函数是异步的,这意味着在最终检查之前状态不会被更改。我用谷歌搜索并搜索了一种在执行代码之前等待状态改变的方法但是空了。我现在已经解决了这个问题并且认为我会把它放在Stack上,所以其他人可能会从我提出的方法中受益。我也想知道我正在做的任何利弊,或者任何可能更好的建议。

当我用Google搜索时,我遇到了一种向setState()发送回调的方法,如下所示:setState(data, callback)。我起初并不认为这对我有用,因为我有多个setState()来电。但是,我意识到我可以将handleSubmit()方法转换为使用三元组的单个setState()调用。像这样:

handleSubmit() {
    this.setState({
      userError: (
        this.refs.user.getValue() ? null : "This field is required"
      ),
      passError: (
        this.refs.pass.getValue() ? null : "This field is required"
      ),
      passConfirmError: (
        (this.refs.pass.getValue() == this.refs.passConfirm.getValue()) ? 
          null : "Passwords do not match"
      )
    }, () => {
      if(!this.state.userError && !this.state.passError && !this.state.passConfirmError)
        alert('worked');
    })
}

在状态改变后将执行匿名回调函数,允许我的检查工作。

我预见到这种方法的唯一问题是嵌套三元组,因为如果需要它们可能会变得非常混乱。这可能会对我的计划产生任何不利影响吗?或者更好的方法我可以解决这个问题?

我希望我帮助一些人解决问题。 :d

1 个答案:

答案 0 :(得分:2)

React documentation鼓励使用componentDidUpdate代替setState的回调参数。

然而,如果您认为代码看起来有点混乱,请尝试本地功能并拨打setState

handleSubmit() {
  const {user, pass, passConfirm} = this.refs;
  const userError = user.getValue() ? null : "This field is required";
  const passError = pass.getValue() ? null : "This field is required";
  const passConfirmError = !passError && pass.getValue() === passConfirm.getValue()
    ? null
    : "Passwords do not match";

  this.setState({userError, passError, passConfirmError});

  if(!userError && !emptyPassError && !passConfirmError)
    alert('worked');
}

最后,documentation还建议在字符串refs:

上使用回调引用
  

使用ref回调只是为了在类上设置属性是很常见的   用于访问DOM元素的模式。如果您目前正在使用   this.refs.myRefName访问refs,我们建议使用此模式   代替。