Form validation for multiple inputs

Posted on

Problem

I created an example of a simple form for multiple inputs (name and phone number) in React. If the user enters invalid data in input field, error text is displayed near the same field. I learned a lot of different examples of the form validation using React and I don’t understand where is the better place for validation checking in form.

import React from 'react'
import { render } from 'react-dom'

const ErrorOutput = props => {
  let name = props.name
  let inputValue = props.case
  if (name === 'firstName') {
    if (!inputValue.match(/^[a-zA-Z]+$/) && inputValue.length > 0) {
        return <span>Letters only</span>
      }
    return <span></span>
  }
  if (name === 'telNo') {
    if(!inputValue.match(/^[0-9]+$/) && inputValue.length > 0) {
        return <span>Numbers only</span>
      }
    return <span></span>
  }
}

class App extends React.Component {
  constructor(props){
    super(props)

    this.state = {
      firstName: '',
      telNo: ''
    }
  }

  handleValidation(e) {    
    this.setState({
      [e.target.name]: e.target.value 
    })  
  }

  render() {
    return (
      <form>
        <label>
          First name:
        </label>
        <input
          type='text'
          name ='firstName'
          value = {this.state.firstName}
          onChange = {this.handleValidation.bind(this)}
        />
        <ErrorOutput case={this.state.firstName} name={'firstName'} />
        <label>
          Phone number:
        </label>
        <input
          type='tel'
          name ='telNo'
          value = {this.state.telNo}
          onChange = {this.handleValidation.bind(this)}
        />
        <ErrorOutput case={this.state.telNo} name={'telNo'} />
      </form>
    )
  }
}

render(
  <App />,
  document.getElementById('root')
)

Solution

A pattern I use for this is individual validators:

function validateName(value) {
  if (value.length > 0) {
    if (!inputValue.match(/^[a-zA-Z]+$/)) {
      return 'Letters only'
    }
  }
  return ''
}

Obviously this can be heavy for simple forms, but it’s a very reusable pattern, and it keeps your components lighter:

handleValidation(e) {
  const name = e.target.name
  const value = e.target.value  
  this.setState({ [name]: value }, () => {
    if (name === 'firstName') {
      this.setState({ errors[name]: validateName(value) })
    }
  })  
}

This allows you to do:

{this.state.errors['firstName'] && <div className="form-error">{this.state.errors['firstName']}</div>}

One benefit I find to this pattern is that it scales well – as your forms grow, you’ll refactor to have your inputs as their own components and managing their own state, and this pattern will scale better and better as you refactor, as opposed to doing all the checking within a single function.

Leave a Reply

Your email address will not be published. Required fields are marked *