0
votes

I have a previous form validation of a controlled form, and I'm trying to convert the class component to a function component using hooks. But I have problems implementing the original onBlur and onChange methods. My original code:

import React, {Component} from 'react';
import { Label, Button, Col, Row, FormFeedback, Input, Form } from 'reactstrap';

function SignUp (){
    return(
        <div className="container">
            <div className="row" style={{'padding': '30px 12px 60px 12px'}}>
            <div className="col col-md-8">
                <h4>Inscríbete y crea tu galería de pesonajes!</h4>
            </div>
        </div>
        <div className="row row-content">
            <div className="col col-md-8">
                {<SignUpForm />}
            </div>
        </div>
    </div>
 );
}
class SignUpForm extends Component {

  constructor(props){
      super(props);
  this.state= {
      firstName: '',
      lastName: '',
      email: '',
      username: '',
      pass: '',
      touched:{
        firstName: false,
        lastName: false,
        email: false,
        pass: false,
      }
  }

  this.handleSubmit = this.handleSubmit.bind(this);
  this.handleInput = this.handleInput.bind(this);
  this.handleBlur = this.handleBlur.bind(this);
  }

  handleInput (e) {
    const target = e.target;
    const value = target.type == 'checkbox'? target.checkbox: target.value
    const name = target.name;

this.setState({
    [name]: value,
})
this.setState({username: this.state.email})
  }

  handleBlur = (field) => (e) => {
      this.setState({
          touched : { ...this.state.touched, [field]: true}
      })
  }

  validate (firstName, lastName, email, pass){
  const validEmail = (val) => /^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,4}$/i.test(val);
  const errors = {
    firstName: '',
    lastName: '',
    email: '',
    password: ''
  }

    if (this.state.touched.firstName && firstName.length < 2)
        errors.firstName = 'Tu nombre debe tener más de 3 caracteres'

    else if (this.state.touched.firstName && firstName.length > 15)
        errors.firstName = 'Tu nombre debe tener menos de 15 caracteres'

    if (this.state.touched.lastName && lastName.length < 2)
        errors.lastName = 'Tu nombre debe tener más de 3 caracteres'

    else if (this.state.touched.lastName && lastName.length > 15)
        errors.lastName = 'Tu nombre debe tener menos de 15 caracteres'

    if (this.state.touched.email && !validEmail(email))
        errors.email = 'Email No valido'

    else if (this.state.touched.pass && pass.length < 4)
        errors.pass = 'Tu password debe tener más de 3 caracteres'

    return errors
}

 handleSubmit(e) {
    //some fetch operation
  }

render(){
    //const {firstName, lastName, email, pass} = this.state
    const errors = this.validate(this.state.firstName, this.state.lastName, this.state.email, this.state.pass)
    return(
      <Form onSubmit={this.handleSubmit}>
        <Row className="form-group">
          <Label htmlFor="firstname" md={2}><strong>Tu Nombre</strong></Label>
          <Col md={4}>
                <Input type="text" id="firstName" name="firstName"
                  placeholder="Juanito"
                  value={this.state.firstName}
                  className="form-control"
                  valid={errors.firstName === ''}
                  invalid={errors.firstName !== ''}
                  onBlur={this.handleBlur('firstName')}
                  onChange={this.handleInput} />
                <FormFeedback>{errors.firstName}</FormFeedback>
            </Col>
            <Label htmlFor="lastname" md={2}><strong>Tu Apellido</strong></Label>
            <Col md={4}>
              <Input type="text" id="lastName" name="lastName"
                  placeholder="Pérez"
                  value={this.state.lastName}
                  className="form-control"
                  valid={errors.lastName === ''}
                  invalid={errors.lastName !== ''}
                  onBlur={this.handleBlur('lastName')}
                  onChange={this.handleInput} />
                <FormFeedback>{errors.lastName}</FormFeedback>
          </Col>
          </Row>
          <Row className="form-group">
          <Label htmlFor="email" md={2}><strong>Email(*)</strong></Label>
          <Col md={4}>
            <Input type="email" id="email" name="email"
                placeholder="[email protected]"
                className="form-control"
                valid={errors.email === ''}
                invalid={errors.email !== ''}
                onBlur={this.handleBlur('email')}
                onChange={this.handleInput}/>
            <FormFeedback>{errors.email}</FormFeedback>
          </Col>
          <Label htmlFor="password" md={2}><strong>Contraseña</strong></Label>
            <Col md={4}>
                <Input type="password" id="pass" name="password"
                    className="form-control"
                    valid={errors.pass === ''}
                    invalid={errors.pass !== ''}
                    onBlur={this.handleBlur('password')}
                    onChange={this.handleInput} />
                <FormFeedback>{errors.pass}</FormFeedback>
            </Col>
        </Row>
        <Row className="form-group">
          <Col md={{size:2, offset:10}}>
              <Button type="submit" value="submit" name="submit" color="primary">
                  Inscribirse
              </Button>
          </Col>
        </Row>
      </Form>
    )
 }
}

export default SignUp;

And Here I tried to implement the same:

import React, { useState } from 'react';
import { Label, Button, Col, Row, FormFeedback, Input, Form } from 'reactstrap';


function SignUp (){
    return(
        <div className="container">
            <div className="row" style={{'padding': '30px 12px 60px 12px'}}>
                <div className="col col-md-8">
                    <h4>Inscríbete y crea tu galería de pesonajes!</h4>
                </div>
            </div>
            <div className="row row-content">
                <div className="col col-md-8">
                    {<SignUpForm />}
                </div>
            </div>
        </div>
    );
}

function SignUpForm() {

    const [formState, setFormState] = useState({firstName: '', lastName: '', email: '', pass: ''})
    const [username, setUsername] = useState('')
    const [touched, setTouched] = useState({firstName: false, lastName: false, email: false, pass: false})

    function handleInput (e) {
        const target = e.target;
        const value = target.type == 'checkbox'? target.checkbox: target.value
        const name = target.name;
        setFormState({ [name]: value })
    }

    function handleBlur(e) { 
        console.log(e.target.name)
        setTouched({...touched, [e.target.name]: true})

    }

    function validate(firstName, lastName, email, pass) {
        const validEmail = (val) => /^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,4}$/i.test(val);
        const errors = {
            firstName: '',
            lastName: '',
            email: '',
            password: ''
        }

        if (touched.firstName && firstName.length <= 2)
            errors.firstName = 'Tu nombre debe tener más de 2 caracteres'

        else if (touched.firstName && firstName.length > 15)
            errors.firstName = 'Tu nombre debe tener menos de 15 caracteres'

        if (touched.lastName && lastName.length < 2)
            errors.lastName = 'Tu apellido debe tener más de 3 caracteres'

        else if (touched.lastName && lastName.length > 15)
            errors.lastName = 'Tu apellido debe tener menos de 15 caracteres'

        if (touched.email && !validEmail(email))
            errors.email = 'Email No valido'

        else if (touched.pass && pass.length < 4)
            errors.pass = 'Tu password debe tener más de 3 caracteres'
        return errors
    }

    function handleSubmit(e) {
        //some fetch operation
    }

    const errors = validate(formState.firstName, formState.lastName, formState.email, formState.pass)
    return(
        <Form onSubmit={ () => handleSubmit}>
            <Row className="form-group">
                <Label htmlFor="firstname" md={2}><strong>Tu Nombre</strong></Label>
                <Col md={4}>
                    <Input type="text" id="firstName" name="firstName"
                        placeholder="Juanito"
                        value={formState.firstName}
                        className="form-control"
                        valid={errors.firstName === ''}
                        invalid={errors.firstName !== ''}
                        onBlur={ (e) => handleBlur(e) }
                        onChange={ (e) => handleInput(e) } />
                    <FormFeedback>{errors.firstName}</FormFeedback>
                </Col>
                <Label htmlFor="lastname" md={2}><strong>Tu Apellido</strong></Label>
                <Col md={4}>
                    <Input type="text" id="lastName" name="lastName"
                        placeholder="Pérez"
                        value={formState.lastName}
                        className="form-control"
                        valid={errors.lastName === ''}
                        invalid={errors.lastName !== ''}
                        onBlur={ (e) => handleBlur(e)}
                        onChange={(e) => handleInput(e)} />
                    <FormFeedback>{errors.lastName}</FormFeedback>
                </Col>
                </Row>
                <Row className="form-group">
                <Label htmlFor="email" md={2}><strong>Email(*)</strong></Label>
                <Col md={4}>
                <Input type="email" id="email" name="email"
                    placeholder="[email protected]"
                    value={formState.email}
                    className="form-control"
                    valid={errors.email === ''}
                    invalid={errors.email !== ''}
                    onBlur={ (e) => handleBlur(e)}
                    onChange={(e) => handleInput(e)} />
                <FormFeedback>{errors.email}</FormFeedback>
                </Col>
                <Label htmlFor="password" md={2}><strong>Contraseña</strong></Label>
                <Col md={4}>
                    <Input type="password" id="pass" name="password"
                        value={formState.pass}
                        className="form-control"
                        valid={errors.pass === ''}
                        invalid={errors.pass !== ''}
                        onBlur={ (e) => handleBlur(e)}
                        onChange={(e) => handleInput(e)} />
                    <FormFeedback>{errors.pass}</FormFeedback>
                </Col>
            </Row>
            <Row className="form-group">
                <Col md={{size:2, offset:10}}>
                    <Button type="submit" value="submit" name="submit" color="primary">
                        Inscribirse
                    </Button>
                </Col>
            </Row>
        </Form>
    )
}

export default SignUp;

When I beggin input values in the second field (lastname) there's and error saying 'firstname' is undefined (cannot read length property of undefined). I think the problem is with the fact that setFirstName doesn't get to set the value before the validation function, but after hourse rearranging the code I can't find a way to fix it

1
setFormState({ ...formState, [name]: value })?Vasyl Butov
@VasylButov There I'm trying to provide previous state and set the new value of the correspondent propertyRodrigo
I mean, your setFormState doesn't spread object, which may cause unpredictable errors. Have you tried it my way?Vasyl Butov
Thanks man! I was so dizzy I hadn't notice that detail!Rodrigo

1 Answers

1
votes

I was struggling with this as well and would like to see this marked as answered because it helped me.

As @VasylButov pointed out, setFormState([name]: value) is over-writing the formState object and setting it to value. Use the spread operator ...formState first, then modifying the property you want setFormState({...formState, [name]: value}).

As a new contributor to SO, I'm not 100% sure what the etiquette is on posting an answer that was already answered in a comment so please forgive me if this isn't correct.