用户登录返回错误时的SetState错误

时间:2017-07-24 00:48:09

标签: reactjs meteor

我创建了一个登录页面,它将用户从公共路由到经过身份验证的路由运行良好。如果登录时出错(例如找不到电子邮件),我会在控制台Warning: setState(...): Can only update a mounted or mounting component. This usually means you called setState() on an unmounted component. This is a no-op. Please check the code for the LoginForm component.中收到错误消息。

我认为这可能与我在App中使用createContainer的方式有关。

我认为问题与流星在从服务器收到回复之前通过Meteor.loggingIn();等于true的方式有关。如果出现错误,true会快速更改为false,我认为这会导致页面重新加载。

我希望能够使用this.setState({ loginError: error.reason });,以便告诉用户出了什么问题。

任何建议。

路径:App.jsx

const App = appProps => (
  <Router>
    <Grid className="main-page-container">
      <Switch>
        <Authenticated exact path="/" component={Home} {...appProps} />
        <Public exact path="/login" component={Login} {...appProps} />
      </Switch>
    </Grid>
  </Router>
);


App.propTypes = {
  loggingIn: PropTypes.bool,
  authenticated: PropTypes.bool
};


export default createContainer(() => {
  const loggingIn = Meteor.loggingIn();
  return {
    loggingIn,
    authenticated: !loggingIn && !!Meteor.userId()
  };
}, App);

路径:Public.jsx

const Public = ({ loggingIn, authenticated, component, ...rest }) => (
  <Route
    {...rest}
    render={(props) => {
      if (loggingIn) return <div />;
      return !authenticated ?
      (React.createElement(component, { ...props, loggingIn, authenticated })) :
      (<Redirect to="/" />);
    }}
  />
);

Public.propTypes = {
  loggingIn: PropTypes.bool,
  authenticated: PropTypes.bool,
  component: PropTypes.func
};

export default Public;

路径:LoginForm.jsx

export default class LoginForm extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      email: '',
      errors: {},
      password: '',
      loginError: ''
    };

    this.handleInputChange = this.handleInputChange.bind(this);
    this.handleSubmit = this.handleSubmit.bind(this);
  }

  handleInputChange(event) {
    const target = event.target;
    const value = target.type === 'checkbox'
      ? target.checked
      : target.value;
    const name = target.name;

    this.setState({[name]: value});
  }

  handleSubmit(event) {
    event.preventDefault();

    this.setState({
      errors: {}
    }, function() {
      var data = {
        email: this.state.email,
        password: this.state.password
      };
      var email = this.state.email;
      var password = this.state.password;

      const errors = loginUserValidation(data);

      if (errors) {
        this.setState({errors: errors});
      } else {
        Meteor.loginWithPassword(email, password, (error) => {
          if (error) {
            this.setState({ loginError: error.reason });
          }
        });
      }
    });
  }

  render() {
    return (
      <div className="registration-form-container">
        <Row>
          <Col sm={8} smOffset={2} md={6} mdOffset={3}>

            <div className="paper">
              <Form onSubmit={this.handleSubmit}>
                <section className="form-title">
                  <h3 className="text-center">Login</h3>
                </section>
                <hr />
                <section className="form-content-login-or-registration">

                  {this.state.loginError &&
                    <div className="alert alert-danger">
                      <p>{this.state.loginError}</p>
                    </div>
                  }

                  <SingleInput
                    name={'email'}
                    inputType={'email'}
                    controlFunc={this.handleInputChange}
                    content={this.state.email}
                    placeholder={'Email'}
                    bsSize={null}
                    error={this.state.errors && this.state.errors.email}
                  />

                  <SingleInput
                    name={'password'}
                    inputType={'password'}
                    controlFunc={this.handleInputChange}
                    content={this.state.password}
                    placeholder={'Password'}
                    bsSize={null}
                    error={this.state.errors && this.state.errors.password}
                  />
                </section>
                <section className="form-buttons">
                  <Button type="submit" className="btn btn-primary" block>Login</Button>
                </section>
              </Form>
            </div>
          </Col>
        </Row>
      </div>
    )
  }
}

1 个答案:

答案 0 :(得分:2)

Meteor.loginWithPasswordlogginIn值更改为true时,您的<Public />组件会卸载已包裹的<LoginForm />

const Public = ({ loggingIn, authenticated, component, ...rest }) => (
  <Route
    {...rest}
    render={(props) => {
      if (loggingIn) return <div />; // <--- this right here
      return !authenticated ?
      (React.createElement(component, { ...props, loggingIn, authenticated })) :
      (<Redirect to="/" />);
    }}
  />
);

因此loggingIn值更改导致<Public />重新呈现。现在loggingIn为真,你渲染一个div而不是组件,卸载它并在错误回调试图调用它时使setState不可用。

编辑:回应你的评论......

为了防止这种情况,您可以处理<LoginForm />

中的错误显示
  1. if (loggingIn) return <div />;组件中的<Route />移除<Public />
  2. <LoginForm />内,您可以处理错误情况。通过制作一个显示错误的组件并将其包含在您希望显示的<LoginForm />组件中来执行此操作。如果没有错误,则使<ErrorDisplay />组件不返回任何内容。
  3. 示例<ErrorDisplay />组件

    const ErrorDisplay = ({ errors }) => {
        errors && <div className="error-container">{ errors }</div>
    };
    

    这显然是准系统,但我希望它能帮助你理解!

相关问题