简体   繁体   中英

ReactJS: How to create a self-validating input field?

I have a form where one of the fields needs a very specific validation. I want to be able to reuse this fields in other forms without the form having to re-implement the validation method again and again.

I thought extracting the input field to its own component, DomainInput would work:

<div id="domain-input-area" class={this.state.showDomain ? '' : 'base-remove'}>
     <DomainInput/>
</div>

DomainInput then contains all the validation logic for itself

import { Component } from 'react';
//import '../../../login-box/login-box.styles.css'
import '../domain-input/domain-input.styles.css';

class DomainInput extends Component{

  constructor(){
    super();

    this.state = {
        domain:'',
        valid:false,
        error:''
    };
  }

  handleDomainInput = (e) => {

    let domain = e.target.value;
    let error = '';
    let valid = true;
  
    if(domain.indexOf('my.salesforce.com') === -1){
        error = `${this.state.domain} is not a valid salesforce domain`;
        valid = false;
        this.setState({error:error,valid:valid});
        
    }
  
    if(domain.indexOf('https://') != 0){
        error = `Please use https://`;
        valid = false;
        this.setState({error:error,valid:valid});;
        
    }

    if(valid){
        domain = domain.trim();
        let lastCharacter = domain.substr(domain.length-1);
        
        //remove last slash
        if(lastCharacter === '/'){
        domain = domain.substr(0,domain.length-1);
        }
    
        this.setState({domain:domain,valid:true});
    }
  }

  render(){
      return (
        <input type="text" placeholder="https://your-domain.my.salesforce.com" class={this.state.valid ? 'base-input valid' : 'base-input invalid'} id="domain-input"  onChange={this.handleDomainInput}/>
      )
  }
}

export default DomainInput

My idea was that when DomainInput knew that its state was valid, it would fire an event to the parent component (the form) to let it know that it has been validated.

But I realised this is not the react way due to one-way data flow, and instead the onChange handler should be implemented in the parent, which as I said, I don't want to do because I want to be able to drop DomainInput in any form, and have it validate itself.

Other frameworks like LWC allow child components to send custom events to the parent. In my case, it would send a validated event and this would in turn set the parent's state to validDomain:true

How do I do this the react way?

To give you an idea, how you can do with parent (manages state) and child (Custom Input Field), Here's sample code:

TextField.jsx

export const TextField = ({error, ...rest}) => {
  return (
    <div className="mb-6">
      <label htmlFor={rest.name} className="">
        {rest.label}
      </label>
      <input
        type="text"
        {...rest}
        className={ '' + (!!error ? ' border-red' : '')}
      />
      {error && <div className="...">{error}</div>}
    </div>
  )
}

ParentComponent.jsx

 this.state = { email: '', error: '' }

 <TextField
        label="Email Address"
        name="email"
        value={this.state.email}
        placeholder="doge@example.com"
        onChange={handleChange}
        onBlur={handleBlur}
        error={state.error}
      />

Now validate your input and set the state accordingly in the parent component using custom handleChange and handleBlur functions as follow:

handleChange = (event) => {
    let { name, value } = event.target
    // set state here and also validate
}

handleBlur = (event) => {
  let { name, value } = event.target
  // set state here and also validate
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM