首页 > 解决方案 > 验证 render() 中使用的状态中的变量以设置样式/填充 html

问题描述

我有以下 React 组件,它包含一个带有两个输入和一个按钮的表单。

export default class Login extends Component {
    constructor(props) {
        super(props);
        this.state = {
            email: null,
            password: null
        }
    }

    emailInputChanged = (e) => {
        this.setState({
            email: e.target.value.trim()
        });
    };
    passwordInputChanged = (e) => {
        this.setState({
            password: e.target.value.trim()
        });
    };

    loginButtonClicked = (e) => {
        e.preventDefault();
        if (!this.isFormValid()) {
            //Perform some API request to validate data
        }
    };

    signupButtonClicked = (e) => {
        e.preventDefault();
        this.props.history.push('/signup');
    };

    forgotPasswordButtonClicked = (e) => {
        e.preventDefault();
        this.props.history.push('/forgot-password');
    };


    isFormValid = () => {
        const {email, password} = this.state;
        if (email === null || password === null) {
            return false;
        }

        return isValidEmail(email) && password.length > 0;
    };

    render() {
        const {email, password} = this.state;
        return (
            <div id="login">
                <h1 className="title">Login</h1>
                <form action="">
                    <div className={(!isValidEmail(email) ? 'has-error' : '') + ' input-holder'}>
                        <Label htmlFor={'loginEmail'} hidden={email !== null && email.length > 0}>email</Label>
                        <input type="text" className="input" id="loginEmail" value={email !== null ? email : ''}
                               onChange={this.emailInputChanged}/>
                    </div>
                    <div className={(password !== null && password.length === 0 ? 'has-error' : '') + ' input-holder'}>
                        <Label htmlFor={'loginPassword'}
                               hidden={password !== null && password.length > 0}>password</Label>
                        <input type="password" className="input" id="loginPassword"
                               value={password !== null ? password : ''}
                               onChange={this.passwordInputChanged}/>
                    </div>
                    <button type="submit" className="btn btn-default" id="loginButton"
                            onClick={this.loginButtonClicked}>
                        login
                    </button>
                </form>
                <div className="utilities">
                    <a href={'/signup'} onClick={this.signupButtonClicked}>don't have an account?</a>
                    <a href={'/forgot-password'} onClick={this.forgotPasswordButtonClicked}>forgot your password?</a>
                </div>
            </div>
        )
    }
}

export function isValidEmail(email) {
    const expression = /\S+@\S+/;
    return expression.test(String(email).toLowerCase());
}

输入的值存储在组件的state. 我已经给了它们初始值并在事件中null使用更新它们。setState()onChange

在该render()方法中,我使用状态为具有无效值的输入着色。目前,我仅根据简单的正则表达式检查电子邮件值,并且密码长度至少为 1 个字符。

我将状态变量的初始值设置为的原因是,null我可以检查布局和输入上的初始样式以不标记为"has-errors". 但是我需要将检查扩展到:

this.state.email !== null && this.state.email.length === 0

为了工作,因为 null 没有length属性。

是否有更清洁和“React-ish”的方式来实现这一点: - 保存输入的 div 的初始状态没有类has-errors - 在输入上设置属性时检查较少value(因为 React 不接受null作为属性的值)


编辑:this.state.email如果and 的初始值this.state.password是空字符串,我将has-error应用于保存输入的 div。在我看来,这是糟糕的用户体验,因为用户没有做任何事情而且他已经错了

hidden属性由我制作的自定义组件使用,它的作用类似于“占位符”(如果在输入中键入内容,则会被删除)。

下面的视频显示了我的表单在何时为空字符串时的样子this.state.email以及this.state.password我的<Label>组件如何工作:

在此处输入图像描述

标签: javascriptreactjs

解决方案


您可以创建一个验证函数,该函数返回一个错误对象以了解哪些字段出错,并使用空字符串作为初始值。我不明白你想用 hidden 属性做什么。

编辑:在状态中添加一个touched属性,以知道哪个字段被触动了。

export default class Login extends Component {
  constructor(props) {
    super(props);
    this.state = {
      email: '',
      password: '',
      touched: {},
    };
  }

  emailInputChanged = e => {
    this.setState({
      email: e.target.value.trim(),
      touched: {
        ...this.state.touched,
        email: true,
      },
    });
  };

  passwordInputChanged = e => {
    this.setState({
      password: e.target.value.trim(),
      touched: {
        ...this.state.touched,
        password: true,
      },
    });
  };

  loginButtonClicked = e => {
    e.preventDefault();
    if (!this.isFormValid()) {
      //Perform some API request to validate data
    }
  };

  isFormValid = () => {
    const errors = this.validate();

    return Object.keys(errors).length === 0;
  };

  validate = () => {
    const errors = {};
    const { email, password } = this.state;

    if (!isValidEmail(email)) {
      errors.email = true;
    }

    if (password.length === 0) {
      errors.password = true;
    }

    return errors;
  };

  render() {
    const { email, password, touched } = this.state;
    const errors = this.validate();

    return (
      <div id="login">
        <h1 className="title">Login</h1>
        <form action="">
          <div
            className={
              (errors.email && touched.email ? 'has-error' : '') +
              ' input-holder'
            }
          >
            <Label htmlFor={'loginEmail'}>email</Label>
            <input
              type="text"
              className="input"
              id="loginEmail"
              value={email}
              onChange={this.emailInputChanged}
            />
          </div>
          <div
            className={
              (errors.password && touched.password ? 'has-error' : '') +
              ' input-holder'
            }
          >
            <Label htmlFor={'loginPassword'}>password</Label>
            <input
              type="password"
              className="input"
              id="loginPassword"
              value={password}
              onChange={this.passwordInputChanged}
            />
          </div>
          <button
            type="submit"
            className="btn btn-default"
            id="loginButton"
            onClick={this.loginButtonClicked}
          >
            login
          </button>
        </form>
      </div>
    );
  }
}

export function isValidEmail(email) {
  const expression = /\S+@\S+/;
  return expression.test(String(email).toLowerCase());
}

推荐阅读