使用 Semantic-UI-React 进行表单验证

Form Validation with Semantic-UI-React

我正在使用官方 Semantic UI React 组件来创建 Web 应用程序。我的注册页面上有一个表单,其中包含一个电子邮件字段、一个密码字段和一个确认密码字段。

import {Component} from 'react';
import {Button, Form, Message} from 'semantic-ui-react';
import {signUp} from '../../actions/auth';

class SignUp extends Component {
    constructor(props) {
        super(props);
        this.handleSubmit = this.handleSubmit.bind(this);
    }
    handleSubmit(e, {formData}) {
        e.preventDefault();

        //
        // Potentially need to manually validate fields here?
        //

        // Send a POST request to the server with the formData
        this.props.dispatch(signUp(formData)).then(({isAuthenticated}) => {
            if (isAuthenticated) {
                // Redirect to the home page if the user is authenticated
                this.props.router.push('/');
            }
        }
    }
    render() {
        const {err} = this.props;

        return (
            <Form onSubmit={this.handleSubmit} error={Boolean(err)}>
                <Form.Input label="Email" name="email" type="text"/>
                <Form.Input label="Password" name="password" type="password"/>
                <Form.Input label="Confirm Password" name="confirmPassword" type="password"/>

                {err &&
                    <Message header="Error" content={err.message} error/>
                }

                <Button size="huge" type="submit" primary>Sign Up</Button>
            </Form>
        );
    }
}

现在,我已经习惯了常规的语义 UI 库,它有一个 Form Validation addon。通常,我会在单独的 JavaScript 文件

中定义这样的规则
$('.ui.form').form({
    fields: {
        email: {
            identifier: 'email',
            rules: [{
                type: 'empty',
                prompt: 'Please enter your email address'
            }, {
                type: 'regExp',
                value: "[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*@(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?",
                prompt: 'Please enter a valid email address'
            }]
        },
        password: {
            identifier: 'password',
            rules: [{
                type: 'empty',
                prompt: 'Please enter your password'
            }, {
                type: 'minLength[8]',
                prompt: 'Your password must be at least {ruleValue} characters'
            }]
        },
        confirmPassword: {
            identifier: 'confirmPassword',
            rules: [{
                type: 'match[password]',
                prompt: 'The password you provided does not match'
            }]
        }
    }
});

是否有类似的方法使用语义 UI React 组件来验证表单?我搜索了文档但没有成功,而且似乎没有使用此语义 UI React 库进行验证的示例。

我是否需要在 handleSubmit 函数中手动验证每个字段?解决此问题的最佳方法是什么?感谢您的帮助!

Do I instead need to validate each field by hand in the handleSubmit function?

悲伤,但真实。苏尔 doesn't have form validation at now. However, you can use HOC to work with forms like redux-form.

在大多数情况下,您必须手动验证表单。但是,RSUI 包含一些工具可以让事情变得更简单,特别是 <Form><Form.Input>

上的错误属性

这是我最近整理的一个表格示例。它可以使用一些重构,但它基本上是通过将每个输入与一个 onChange() 函数绑定到状态,并将回调传递给控制加载屏幕可见性和 "Success. Thank you for submitting" 的提交函数来工作的表格的一部分。

export default class MeetingFormModal extends Component {

  constructor(props) {
    super(props)

    this.state = {
      firstName: '',
      lastName: '',
      email: '',
      location: '',
      firstNameError: false,
      lastNameError: false,
      emailError: false,
      locationError: false,
      formError: false,
      errorMessage: 'Please complete all required fields.',
      complete: false,
      modalOpen: false
    }

    this.submitMeetingForm = this.submitMeetingForm.bind(this);
    this.successCallback = this.successCallback.bind(this);
  }


  successCallback() {
    this.setState({
      complete: true
    })
    setTimeout( () => {this.setState({modalOpen: false})}, 5000);
    this.props.hideLoading();
  }

  handleClose = () => this.setState({ modalOpen: false })
  handleOpen = () => this.setState({ modalOpen: true })

  submitMeetingForm() {

    let error = false;

    if (this.state.studentFirstName === '') {
      this.setState({firstNameError: true})
      error = true
    } else {
      this.setState({firstNameError: false})
      error = false
    }
    if (this.state.studentLastName === '') {
      this.setState({lastNameError: true})
      error = true
    } else {
      this.setState({lastNameError: false})
      error = false
    }
    if (this.state.email === '') {
      this.setState({emailError: true})
      error = true
    } else {
      this.setState({emailError: false})
      error = false
    }
    if (this.state.location === '') {
      this.setState({locationError: true})
      error = true
    } else {
      this.setState({locationError: false})
      error = false
    }

    if (error) {
      this.setState({formError: true})
      return
    } else {
      this.setState({formError: false})
    }


    let meeting = {
      first_name: this.state.firstName,
      last_name: this.state.lastName,
      email: this.state.email,
      location: this.state.location,

    this.props.createMeeting(meeting, this.successCallback)
    this.props.showLoading();
  }

  capitalize(string) {
    return string.charAt(0).toUpperCase() + string.slice(1);
  }

  render() {
    return(
      <Modal
        trigger={<Button onClick={this.handleOpen} basic color='blue'>Schedule Now</Button>}
        open={this.state.modalOpen}
        onClose={this.handleClose}
        closeIcon={true}
      >
        <Modal.Header>Schedule Your Interview</Modal.Header>
        <Modal.Content>
          {!this.state.complete ?
          <Modal.Description>
            <Form error={this.state.formError}>
              <Form.Group widths='equal'>
                <Form.Field>
                  <Form.Input required={true} onChange={(e) => this.setState({firstName: e.target.value})} label='First Name' placeholder="First Name..." error={this.state.firstNameError}/>
                </Form.Field>
                <Form.Field>
                  <Form.Input required={true} onChange={(e) => this.setState({lastName: e.target.value})} label='Last Name' placeholder="Last Name..." error={this.state.lastNameError}/>
                </Form.Field>
              </Form.Group>
              <Form.Field >
                <Form.Input required={true} onChange={(e) => this.setState({email: e.target.value})} label='Email' placeholder="Email..." error={this.state.emailError}/>
              </Form.Field>
              <Form.Field>
                <Form.Input required={true} onChange={(e) => this.setState({location: e.target.value})} label='Location' placeholder='City, State/Province, Country...' error={this.state.locationError}/>
              </Form.Field>
            </Form>
          </Modal.Description>
          : 
            <div className='modal-complete'>
              <Image src='/images/check.png' />
              <p>Thanks for scheduling a meeting, {this.capitalize(this.state.name)}. We've received your information and we'll be in touch shortly.</p>
            </div>
          }
        </Modal.Content>
        {!this.state.complete ?
        <Modal.Actions>
          <Button color='red' onClick={this.handleClose}>Close</Button>
          <Button positive icon='checkmark' labelPosition='right' content="Submit" onClick={this.submitMeetingForm} />
        </Modal.Actions>
        : null }
      </Modal>
    )
  }
}

希望对您有所帮助!

您可以使用插件进行验证。 插件名称:formsy-semantic-ui-react

下面的代码实质上是为每个组件名称和相关值设置状态。 (即,状态可能看起来像 {marketSide:buy, price:50, quantity:9} 我也将表单错误信息填充到状态中,利用 yup 的默认行为不验证未提及的字段通过验证架构。

要点:

1) 对 schema.validate(someObjectToValidate, yupProperties) 的调用(其中 someObjectToValidate 只是 this.state),应该传入 {abortEarly:false} 作为属性对象,以覆盖 yups 默认值一旦遇到单个错误就停止验证的行为,因为我们的表单的消息组件会向用户显示所有错误。

2)如果 yup 验证失败,yup 会抛出一个异常,所以我们捕获异常并挑选出我们感兴趣的错误信息,并用这个错误更新我们的状态。

3)我们必须使用 this.setState(...) 的 'callback form',因为 setState 是异步的,因此状态对象的验证仅在状态更新后发生。

4) 如果 yup 验证成功,我们清除我们的错误和 errorPath 数组。

5) 这是一个快速的解决方案,我在每次渲染时多次检查 errorPaths 数组。最好将 errorPath 和错误信息存储在由组件名称键入的 json 对象中,而不是存储在两个数组中,以改进当前解决方案的(2 个数组 * N 个字段 * N 个潜在错误 = O( 2n^2) 表现。

6)忽略长样式的“<Form.Field control={Radio}>”符号,您可以使用较短的 <Input>, <Button>, <Radio>, 等样式。此语法与验证无关。也忽略 div。

import React, { Component } from 'react'
import { Button, Checkbox, Form, Input, Radio, Select, Message,  Label } from 'semantic-ui-react'
import * as yup from 'yup';


const options = [
  { text: 'buy', value: 'buy' },
  { text: 'sell', value: 'sell' },
]

class OrderEntryV3 extends Component {
  state = {
      errorPaths:[],
      errors:[]
  }

  constructor(props){
      super(props);
  }


  schema = yup.object().shape({
    quantity: yup.number().required().positive().integer(),
    price:  yup.number().required().positive(),
    marketSide: yup.string().required(),
    orderType : yup.string().required()
  });



  handleChange = (e, component) => {
      this.setState({[component.name]:component.value}, ()=>this.schema.validate(this.state, {abortEarly:false})
         .then(valid=>this.setState({errorPaths:[], errors:[]})) //called if the entire form is valid
         .catch(err=>this.setState({errors:err.errors, errorPaths: err.inner.map(i=>i.path) }))) //called if any field is invalid
    };


  render() {

    return (
<div id="oeform-content">
    <div id="oeform-left">
      <Form>
          <Form.Field  error={this.state.errorPaths.includes('marketSide')} name="marketSide" control={Select} label='Market side' options={options} placeholder='market side' onChange={this.handleChange}/>
          <Form.Field  error={this.state.errorPaths.includes('quantity')} type='number' name="quantity" control={Input} label='Quantity' placeholder='quantity' onChange={this.handleChange}/>

          <Form.Group>
              <label><b>Order type</b></label>  
        <Form.Field error={this.state.errorPaths.includes('orderType')} >
          <Radio
            label='market'
            name='orderType'
            value='market'
            checked={this.state.orderType === 'market'}
            onChange={this.handleChange}
          />
        </Form.Field>
        <Form.Field error={this.state.errorPaths.includes('orderType')}>
          <Radio
            label='limit'
            name='orderType'
            value='limit'
            checked={this.state.orderType === 'limit'}
            onChange={this.handleChange}
          />
        </Form.Field>
    </Form.Group>
        <Form.Field error={this.state.errorPaths.includes('price')} name='price' control={Input} type='number' label='Price' placeholder='price' onChange={this.handleChange}/>
        <Form.Field control={Button} disabled={!!this.state.errors.length}>Submit</Form.Field>
        <Message visible={!!this.state.errors.length} warning
        header='Please correct the following issues: '
        list={this.state.errors}/>
      </Form>
    </div>
    <div id="oeform-right">
        <p>{JSON.stringify(this.state)}</p>
    </div>
</div>
    )
  }
}

export default OrderEntryV3

我们甚至有更好的选择,尽管语义-ui-react 没有提供 -> Formik + yup
Formik:帮助管理表单状态 是的:有助于验证该状态

我有下面的组件,它基本上是一个使用 semantic-ui-react 创建的编辑表单。

import React, { Component } from "react";
import { Button, Form, Modal, Message, Divider } from "semantic-ui-react";
import { Formik } from "formik";
import * as yup from "yup";


class EditAboutGrid extends Component {

  render() {
    const {
      userBasic,
      editBasicModal,
      closeModal
    } = this.props;

    return (
      <Formik
        initialValues={{
          firstName: userBasic.firstName,
          lastName: userBasic.lastName,
          bio: userBasic.bio,
        }}
        validationSchema={yup.object().shape({
          firstName: yup
            .string()
            .required("Name cannot be empty"),
          lastName: yup
            .string()
            .required("Name cannot be empty"),
          bio: yup
            .string()
            .max(1000, "Maximum characters exceed 1000")
            .nullable()
        })}
        onSubmit={(values, actions) => {
          //do your stuff here like api calls
        }}
        render={({
          values,
          errors,
          handleChange,
          handleSubmit,
          isSubmitting,
          dirty,
          setFieldValue
        }) => (
          <Modal open={editBasicModal} size="small">
            <Modal.Header>Your basic details</Modal.Header>
            <Modal.Content scrolling>
              {errors.firstName && <Message error content={errors.firstName} />}
              {errors.lastName && <Message error content={errors.lastName} />}
              {errors.bio && <Message error content={errors.bio} />}

              <Form loading={isSubmitting}>
                <Form.Group inline widths="equal">
                  <Form.Input
                    required
                    label="First Name"
                    fluid
                    type="text"
                    name="firstName"
                    value={values.firstName}
                    onChange={handleChange}
                    error={errors.firstName !== undefined}
                  />
                  <Form.Input
                    required
                    label="Last Name"
                    fluid
                    type="text"
                    name="lastName"
                    value={values.lastName}
                    onChange={handleChange}
                    error={errors.lastName !== undefined}
                  />
                </Form.Group>
                <Form.TextArea
                  label="Bio"
                  type="text"
                  name="bio"
                  value={values.bio}
                  onChange={handleChange}
                  rows={3}
                  error={errors.bio !== undefined}
                />
              </Form>
            </Modal.Content>
            <Modal.Actions open={true}>
              <Button
                onClick={() => (dirty ? closeModal(true) : closeModal(false))}>
                Cancel
              </Button>
              <Button
                primary
                type="submit"
                onClick={handleSubmit}
                loading={isSubmitting}
                disabled={isSubmitting || !isEmpty(errors) || !dirty}>
                Update
              </Button>
            </Modal.Actions>
          </Modal>
        )}
      />
    );
  }
}

并且调用此表单使用:

  <EditAboutGrid
    editBasicModal={this.state.editBasicModal}
    userBasic={this.state.user.basic}
    closeModal={this.closeModal}
  />

initialValues 是事情开始的地方。在这里,您将 initial/default 值传递给表单的输入。 values(in form) 将从该默认值中选取数据值。

validationSchema 是使用 yup

进行所有验证的地方

onSubmit 将在表单提交时调用。

使用 Formik + yup 处理表单非常简单。我很喜欢它。

我知道这已经有几年的历史了,并不是所问问题的完整解决方案,但今天看到的任何人都可能会发现这很有用,因为我一直在寻找相同的东西并且 运行 进入这个线程。虽然 semantic-ui-react 没有我能找到的表单验证,但它有一个很好的方式来显示表单验证错误。您仍然必须自己进行验证。请参阅 semantic-ui-react forms documentation page for an example of how it shows validation errors. It also has a feature to show success message. I have created an example 了解如何根据状态控制错误消息。在此示例中,您可以使用“显示性别错误”复选框来切换性别输入的错误消息。

对于语义验证形式-ui你可以简单地实现而不需要任何插件

<Form.Field
      id='form-input-control-project-name'
      control={Input}
      label='Project Name'
      placeholder='Project name'
      onChange={this.handleChange}
      error={this.state.projectName}
    />

this.state.projectName 在这个变量中你必须存储false/error消息

如果值为false不显示错误否则显示错误

我知道这个问题已经有好几年了,但这是我在使用(相对)较新的 React 功能组件和 Hooks 时遇到的困难 API。就我而言,我只是想验证用户输入的电子邮件地址是否有效。我最终通过下面的代码得到了我正在寻找的行为。

import React, {useState} from 'react';
import { Form } from 'semantic-ui-react'
import EmailValidator from 'email-validator';


function MyFormComponentExample() {
    const [emailInput, setEmail] = useState("");
    const [validEmail, validateEmail] = useState(true);

    return (
    <Form>
        <Form.Input
            icon='envelope'
            iconPosition='left'
            label='Email'
            placeholder='Email Address'
            required
            value={emailInput}
            onChange={e => {
                setEmail(e.target.value);                               
                validateEmail(EmailValidator.validate(e.target.value));
            }}
            error={validEmail ? false : {
                content: 'Please enter a valid email address.',
                pointing: 'below'
            }}
        />
    </Form>
    );
}

export default MyFormComponentExample;

弄清楚后,我认为这个结构足够简单,但如果有人对执行此操作的更好模式或方法有任何反馈,我很乐意听到!

您可以使用 Formik 进行验证。

并且我创建了一个库来绑定 Formik 和 Semantic UI React。

https://github.com/JT501/formik-semantic-ui-react

一个简单的例子: Codesandbox