Form Validation with Semantic-UI-React

36,059

Solution 1

We even have a better option, though not provided by semantic-ui-react -> Formik + yup
Formik: helps in managing form state Yup: helps in the validation of that state

I have the below component which is basically an edit form created using 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>
        )}
      />
    );
  }
}

And this form is called using:

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

initialValues is the place where the things start. Here you pass on the initial/default values to the inputs of your form. values(in form) will pick data value from this default.

validationSchema is the place where all validation happens using yup

onSubmit would be called on the form submission.

Handling form using Formik + yup is very easy. I am loving it.

Solution 2

For the most part, you have to validate forms by hand. However, RSUI includes a couple of tools to make things a bit easier, in particular the error prop on <Form> and <Form.Input>

Here's an example of a form I put together recently. It could use a bit of refactoring, but it basically works by tying each input to state with an onChange() function, and passing a callback to the submit function which controls the visibility of the loading screen and the "Success. Thank you for submitting" portion of the form.

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>
    )
  }
}

Hope that helps!

Solution 3

I know this question is several years old, but this was something I struggled with doing while using the (relatively) newer React functional components and Hooks API. In my case, I just wanted to validate that the user input was a valid email address. I ended up getting the behavior I was looking for with the code below.

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;

Once I figured it out, I thought this structure was simple enough, but if anyone has feedback about a better pattern or approach to perform this I would love to hear it!

Solution 4

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

Sad, but true. SUIR doesn't have form validation at now. However, you can use HOC to work with forms like redux-form.

Solution 5

You can use plugin for validation. Plugin name : formsy-semantic-ui-react

Share:
36,059
TFischer
Author by

TFischer

I am a senior software developer who specializes in JavaScript, TypeScript, React, NodeJS, and C#

Updated on November 03, 2020

Comments

  • TFischer
    TFischer over 3 years

    I am using the official Semantic UI React components to create a web application. I have a form on my sign up page, which contains an email field, a password field, and a confirm password field.

    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>
            );
        }
    }
    

    Now, I am used to the regular Semantic UI library, which has a Form Validation addon. Usually, I would define the rules like so in a separate JavaScript file

    $('.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'
                }]
            }
        }
    });
    

    Is there a similar method using the Semantic UI React components for validating the form? I've searched through the documentation without any success, and there doesn't seem to be examples of validation using this Semantic UI React library.

    Do I instead need to validate each field by hand in the handleSubmit function? What is the best way to fix this problem? Thanks for the help!