Wie kann man Eingabewerte mit React.JS richtig validieren?

Lesezeit: 15 Minuten

Ich habe ein einfaches Formular. Alle Komponenten und Zustände werden in der Page-Komponente gespeichert. Es gibt 2 Anzeigeköpfe und 3 Eingabefelder. Die erste Eingabe soll Text sein, und die zweite und dritte sollen Ints sein. Wenn der Benutzer den falschen Datentyp eingibt, möchte ich, dass neben dem Eingabefeld eine Fehlermeldung angezeigt wird. Meine Fragen beziehen sich auf Best Practices in React.JS

Wer entscheidet, dass der Wert gültig ist? Ich nehme an, dass die einzige Aufgabe des Eingabefelds darin besteht, den Wert zurück an die Komponente zu leiten, die den Status enthält. Bedeutet dies also, dass nur Page feststellen kann, ob ein Wert gültig ist?

Wie soll ich dann das Popup erscheinen lassen? Sollte Page ein neues boolesches Zustandselement auslösen, das durch Perp geleitet wird, das Adaptive_Input anweist, die Fehlermeldung anzuzeigen?

JSFiddle

JS:

/**
 * @jsx React.DOM
 */
var Adaptive_Input = React.createClass({ 
    handle_change: function(){
        var new_text = this.refs.input.getDOMNode().value;
        this.props.on_Input_Change(new_text);
    },
    render: function(){
        return (
                <div className="adaptive_placeholder_input_container">
                    <input 
                        className="adaptive_input"
                        type="text" 
                        required="required" 
                        onChange= {this.handle_change}
                        ref="input"
                    ></input>
                    <label
                        className="adaptive_placeholder"
                        alt={this.props.initial}
                        placeholder={this.props.focused}
                    ></label>
                </div>              
                );
    }
});

var Form = React.createClass({
    render: function(){
        return (
                <form>
                    <Adaptive_Input
                        initial={'Name Input'}
                        focused={'Name Input'}
                        on_Input_Change={this.props.handle_text_input}
                    />
                    <Adaptive_Input
                        initial={'Value 1'}
                        focused={'Value 1'}
                        on_Input_Change={this.props.handle_value_1_input}
                    />
                    <Adaptive_Input
                        initial={'Value 2'}
                        focused={'Value 2'}
                        on_Input_Change={this.props.handle_value_2_input}
                    />
                </form>
                );
    }
});

var Page = React.createClass({
    getInitialState: function(){
        return {
            Name : "No Name",
            Value_1 : '0',
            Value_2 : '0',
            Display_Value: '0'
        };
    },
    handle_text_input: function(new_text){
        this.setState({
                Name: new_text
            });
    },
    handle_value_1_input: function(new_value){
        console.log("===");
        var updated_display = parseInt(new_value) + parseInt(this.state.Value_2);
        updated_display = updated_display.toString();
        this.setState({
                Display_Value: updated_display 
            });
    },
    handle_value_2_input: function(new_value){
        var updated_display = parseInt(this.state.Value_1) + parseInt(new_value);
        updated_display = updated_display.toString();
        this.setState({
                Display_Value: updated_display
            });
    },
    render: function(){
        return(
                <div>
                    <h2>{this.state.Name}</h2>
                    <h2>Value 1 + Value 2 = {this.state.Display_Value}</h2>
                    <Form
                        handle_text_input={this.handle_text_input}
                        handle_value_1_input = {this.handle_value_1_input}
                        handle_value_2_input = {this.handle_value_2_input}
                    />
                </div>
        );
    }
});

React.renderComponent(<Page />, document.body);

Benutzer-Avatar
Markus Bolusmjak

Zunächst ist hier ein Beispiel für das, was ich unten erwähnen werde: http://jsbin.com/rixido/2/edit

Wie kann man Eingabewerte mit React.JS richtig validieren?

Wie auch immer du willst. React dient zum Rendern eines Datenmodells. Das Datenmodell sollte wissen, was gültig ist oder nicht. Sie können Backbone-Modelle, JSON-Daten oder alles andere verwenden, um die Daten und ihren Fehlerzustand darzustellen.

Genauer:

React ist im Allgemeinen agnostisch gegenüber Ihren Daten. Es dient zum Rendern und Behandeln von Ereignissen.

Die zu befolgenden Regeln sind:

  1. Elemente können ihren Zustand ändern.
  2. Sie können keine Requisiten ändern.
  3. Sie können einen Rückruf aufrufen, der die Requisiten der obersten Ebene ändert.

Wie entscheidet man, ob etwas eine Requisite oder ein Zustand sein soll? Bedenken Sie Folgendes: Würde irgendein Teil Ihrer App außer dem Textfeld wissen wollen, dass der eingegebene Wert falsch ist? Wenn nein, machen Sie es zu einem Zustand. Wenn ja, sollte es eine Stütze sein.

Wenn Sie beispielsweise möchten, dass eine separate Ansicht “Sie haben 2 Fehler auf dieser Seite” rendert. dann müsste Ihr Fehler einem Toplevel-Datenmodell bekannt sein.

Wo soll dieser Fehler wohnen?
Wenn Ihre App beispielsweise Backbone-Modelle rendert, hätte das Modell selbst eine validate()-Methode und eine validateError-Eigenschaft, die Sie verwenden könnten. Sie könnten andere intelligente Objekte rendern, die dasselbe tun könnten. React sagt auch, dass man versuchen sollte, Requisiten auf ein Minimum zu beschränken und den Rest der Daten zu generieren. Wenn Sie also einen Validator (z https://github.com/flatiron/revalidator) dann könnten Ihre Validierungen heruntersickern und jede Komponente könnte Requisiten mit ihrer übereinstimmenden Validierung überprüfen, um zu sehen, ob sie gültig ist.

Es liegt weitgehend an Ihnen.

(Ich bin persönlich Verwenden von Backbone-Modellen und Rendern in React. Ich habe eine Fehlerwarnung der obersten Ebene, die ich zeige, wenn irgendwo ein Fehler auftritt, und den Fehler beschreibe.)

  • „React sagt auch, versuche Requisiten auf ein Minimum zu beschränken“ – Weißt du, wo React dies dokumentiert?

    – Mark Lundin

    10. Juli 2014 um 14:11 Uhr

  • „Finden Sie heraus, was die absolute Minimaldarstellung des Zustands Ihrer Anwendung sein muss, und berechnen Sie alles andere, was Sie benötigen, bei Bedarf.“ facebook.github.io/react/docs/thinking-in-react.html Schritt 3

    – Mark Bolusmjak

    10. Juli 2014 um 14:48 Uhr

  • Sollte es nicht heißen “React sagt auch, versuche, den Zustand auf ein Minimum zu beschränken”?

    – Mose Lee

    8. April 2015 um 17:13 Uhr

  • @MosesLee Die Verwendung von Requisiten anstelle des Komponentenstatus hält den Status pro Komponente auf einem Minimum. Das Berechnen abgeleiteter Werte in Renderaufrufen, anstatt sie zu speichern, hält Requisiten (und Status) auf einem Minimum. Wir versuchen, den Zustand in beiden Richtungen auf ein Minimum zu beschränken, und die Requisiten im zweiten Sinne auf ein Minimum.

    – Mark Bolusmjak

    8. April 2015 um 17:21 Uhr

Benutzer-Avatar
STAHL

Sie können verwenden npm install --save redux-form

Ich schreibe ein einfaches E-Mail- und Submit-Button-Formular, das die E-Mail validiert und das Formular absendet. Mit redux-form führt das Formular standardmäßig event.preventDefault() für die HTML-onSubmit-Aktion aus.

import React, {Component} from 'react';
import {reduxForm} from 'redux-form';

class LoginForm extends Component {
  onSubmit(props) {
    //do your submit stuff
  }


  render() {
    const {fields: {email}, handleSubmit} = this.props;

    return (
      <form onSubmit={handleSubmit(this.onSubmit.bind(this))}>
        <input type="text" placeholder="Email"
               className={`form-control ${email.touched && email.invalid ? 'has-error' : '' }`}
          {...email}
        />
          <span className="text-help">
            {email.touched ? email.error : ''}
          </span>
        <input type="submit"/>
      </form>
    );
  }
}

function validation(values) {
  const errors = {};
  const emailPattern = /(.+)@(.+){2,}\.(.+){2,}/;
  if (!emailPattern.test(values.email)) {
    errors.email="Enter a valid email";
  }

  return errors;
}

LoginForm = reduxForm({
  form: 'LoginForm',
  fields: ['email'],
  validate: validation
}, null, null)(LoginForm);

export default LoginForm;

  • Na und? Die Leute kommen immer noch zu dieser Frage und wenn es einen neuen Ansatz gibt, um das Problem anzugehen, warum nicht? Redux oder nicht – es ist völlig gültig.

    – mp31415

    29. September 2016 um 13:50 Uhr

Benutzer-Avatar
Tanguy_k

Ihre jsfiddle funktioniert nicht mehr. Ich habe es behoben: http://jsfiddle.net/tkrotoff/bgC6E/40/ mit den Klassen React 16 und ES6.

class Adaptive_Input extends React.Component {
  handle_change(e) {
    var new_text = e.currentTarget.value;
    this.props.on_Input_Change(new_text);
  }

  render() {
    return (
      <div className="adaptive_placeholder_input_container">
        <input
          className="adaptive_input"
          type="text"
          required="required"
          onChange={this.handle_change.bind(this)} />
        <label
          className="adaptive_placeholder"
          alt={this.props.initial}
          placeholder={this.props.focused} />
      </div>
    );
  }
}

class Form extends React.Component {
  render() {
    return (
      <form>
        <Adaptive_Input
          initial={'Name Input'}
          focused={'Name Input'}
          on_Input_Change={this.props.handle_text_input} />

        <Adaptive_Input
          initial={'Value 1'}
          focused={'Value 1'}
          on_Input_Change={this.props.handle_value_1_input} />

        <Adaptive_Input
          initial={'Value 2'}
          focused={'Value 2'}
          on_Input_Change={this.props.handle_value_2_input} />
      </form>
    );
  }
}

class Page extends React.Component {
  constructor(props) {
    super(props);

    this.state = {
      Name: 'No Name',
      Value_1: '0',
      Value_2: '0',
      Display_Value: '0'
    };
  }

  handle_text_input(new_text) {
    this.setState({
      Name: new_text
    });
  }

  handle_value_1_input(new_value) {
    new_value = parseInt(new_value);
    var updated_display = new_value + parseInt(this.state.Value_2);
    updated_display = updated_display.toString();
    this.setState({
      Value_1: new_value,
      Display_Value: updated_display
    });
  }

  handle_value_2_input(new_value) {
    new_value = parseInt(new_value);
    var updated_display = parseInt(this.state.Value_1) + new_value;
    updated_display = updated_display.toString();
    this.setState({
      Value_2: new_value,
      Display_Value: updated_display
    });
  }

  render() {
    return(
      <div>
        <h2>{this.state.Name}</h2>
        <h2>Value 1 + Value 2 = {this.state.Display_Value}</h2>
        <Form
          handle_text_input={this.handle_text_input.bind(this)}
          handle_value_1_input={this.handle_value_1_input.bind(this)}
          handle_value_2_input={this.handle_value_2_input.bind(this)}
        />
      </div>
    );
  }
}

ReactDOM.render(<Page />, document.getElementById('app'));

Und jetzt wird derselbe Code dank dieser Bibliothek mit der Formularvalidierung gehackt: https://github.com/tkrotoff/react-form-with-constraints
=> http://jsfiddle.net/tkrotoff/k4qa4heg/

http://jsfiddle.net/tkrotoff/k4qa4heg/

const { FormWithConstraints, FieldFeedbacks, FieldFeedback } = ReactFormWithConstraints;

class Adaptive_Input extends React.Component {
  static contextTypes = {
    form: PropTypes.object.isRequired
  };

  constructor(props) {
    super(props);

    this.state = {
      field: undefined
    };

    this.fieldWillValidate = this.fieldWillValidate.bind(this);
    this.fieldDidValidate = this.fieldDidValidate.bind(this);
  }

  componentWillMount() {
    this.context.form.addFieldWillValidateEventListener(this.fieldWillValidate);
    this.context.form.addFieldDidValidateEventListener(this.fieldDidValidate);
  }

  componentWillUnmount() {
    this.context.form.removeFieldWillValidateEventListener(this.fieldWillValidate);
    this.context.form.removeFieldDidValidateEventListener(this.fieldDidValidate);
  }

  fieldWillValidate(fieldName) {
    if (fieldName === this.props.name) this.setState({field: undefined});
  }

  fieldDidValidate(field) {
    if (field.name === this.props.name) this.setState({field});
  }

  handle_change(e) {
    var new_text = e.currentTarget.value;
    this.props.on_Input_Change(e, new_text);
  }

  render() {
    const { field } = this.state;
    let className="adaptive_placeholder_input_container";
    if (field !== undefined) {
      if (field.hasErrors()) className += ' error';
      if (field.hasWarnings()) className += ' warning';
    }

    return (
      <div className={className}>
        <input
          type={this.props.type}
          name={this.props.name}
          className="adaptive_input"
          required
          onChange={this.handle_change.bind(this)} />
        <label
          className="adaptive_placeholder"
          alt={this.props.initial}
          placeholder={this.props.focused} />
      </div>
    );
  }
}

class Form extends React.Component {
  constructor(props) {
    super(props);

    this.state = {
      Name: 'No Name',
      Value_1: '0',
      Value_2: '0',
      Display_Value: '0'
    };
  }

  handle_text_input(e, new_text) {
    this.form.validateFields(e.currentTarget);

    this.setState({
      Name: new_text
    });
  }

  handle_value_1_input(e, new_value) {
    this.form.validateFields(e.currentTarget);

    if (this.form.isValid()) {
      new_value = parseInt(new_value);
      var updated_display = new_value + parseInt(this.state.Value_2);
      updated_display = updated_display.toString();
      this.setState({
        Value_1: new_value,
        Display_Value: updated_display
      });
    }
    else {
      this.setState({
        Display_Value: 'Error'
      });
    }
  }

  handle_value_2_input(e, new_value) {
    this.form.validateFields(e.currentTarget);

    if (this.form.isValid()) {
      new_value = parseInt(new_value);
      var updated_display = parseInt(this.state.Value_1) + new_value;
      updated_display = updated_display.toString();
      this.setState({
        Value_2: new_value,
        Display_Value: updated_display
      });
    }
    else {
      this.setState({
        Display_Value: 'Error'
      });
    }
  }

  render() {
    return(
      <div>
        <h2>Name: {this.state.Name}</h2>
        <h2>Value 1 + Value 2 = {this.state.Display_Value}</h2>

        <FormWithConstraints ref={form => this.form = form} noValidate>
          <Adaptive_Input
            type="text"
            name="name_input"
            initial={'Name Input'}
            focused={'Name Input'}
            on_Input_Change={this.handle_text_input.bind(this)} />
          <FieldFeedbacks for="name_input">
            <FieldFeedback when="*" error />
            <FieldFeedback when={value => !/^\w+$/.test(value)} warning>Should only contain alphanumeric characters</FieldFeedback>
          </FieldFeedbacks>

          <Adaptive_Input
            type="number"
            name="value_1_input"
            initial={'Value 1'}
            focused={'Value 1'}
            on_Input_Change={this.handle_value_1_input.bind(this)} />
          <FieldFeedbacks for="value_1_input">
            <FieldFeedback when="*" />
          </FieldFeedbacks>

          <Adaptive_Input
            type="number"
            name="value_2_input"
            initial={'Value 2'}
            focused={'Value 2'}
            on_Input_Change={this.handle_value_2_input.bind(this)} />
          <FieldFeedbacks for="value_2_input">
            <FieldFeedback when="*" />
          </FieldFeedbacks>
        </FormWithConstraints>
      </div>
    );
  }
}

ReactDOM.render(<Form />, document.getElementById('app'));

Die hier vorgeschlagene Lösung ist hackig, da ich versucht habe, sie nahe an der ursprünglichen jsfiddle zu halten. Überprüfen Sie für eine ordnungsgemäße Formularvalidierung mit React-form-with-constraints https://github.com/tkrotoff/react-form-with-constraints#examples

Benutzer-Avatar
VISHAL DAGA

ich habe geschrieben Diese Bibliothek Damit können Sie Ihre Formularelementkomponenten umschließen und Ihre Validatoren im folgenden Format definieren:

<Validation group="myGroup1"
    validators={[
            {
             validator: (val) => !validator.isEmpty(val),
             errorMessage: "Cannot be left empty"
            },...
        }]}>
            <TextField value={this.state.value}
                       className={styles.inputStyles}
                       onChange={
                        (evt)=>{
                          console.log("you have typed: ", evt.target.value);
                        }
                       }/>
</Validation>

Verwenden onChange={this.handleChange.bind(this, "name") Methode u value={this.state.fields["name"]} auf dem Eingabetextfeld und darunter ein Span-Element erstellen, um den Fehler anzuzeigen, siehe das folgende Beispiel.

export default class Form extends Component {

  constructor(){
    super()
    this.state ={
       fields: {
         name:'',
         email: '',
         message: ''
       },
       errors: {},
       disabled : false
    }
  }

  handleValidation(){
       let fields = this.state.fields;
       let errors = {};
       let formIsValid = true;

       if(!fields["name"]){
          formIsValid = false;
          errors["name"] = "Name field cannot be empty";
       }

       if(typeof fields["name"] !== "undefined" && !fields["name"] === false){
          if(!fields["name"].match(/^[a-zA-Z]+$/)){
             formIsValid = false;
             errors["name"] = "Only letters";
          }
       }

       if(!fields["email"]){
          formIsValid = false;
          errors["email"] = "Email field cannot be empty";
       }

       if(typeof fields["email"] !== "undefined" && !fields["email"] === false){
          let lastAtPos = fields["email"].lastIndexOf('@');
          let lastDotPos = fields["email"].lastIndexOf('.');

          if (!(lastAtPos < lastDotPos && lastAtPos > 0 && fields["email"].indexOf('@@') === -1 && lastDotPos > 2 && (fields["email"].length - lastDotPos) > 2)) {
             formIsValid = false;
             errors["email"] = "Email is not valid";
           }
      }

      if(!fields["message"]){
         formIsValid = false;
         errors["message"] = " Message field cannot be empty";
      }

      this.setState({errors: errors});
      return formIsValid;
  }

  handleChange(field, e){
      let fields = this.state.fields;
      fields[field] = e.target.value;
      this.setState({fields});
  }

  handleSubmit(e){
      e.preventDefault();
      if(this.handleValidation()){
          console.log('validation successful')
        }else{
          console.log('validation failed')
        }
  }

  render(){
    return (
      <form onSubmit={this.handleSubmit.bind(this)} method="POST">
          <div className="row">
            <div className="col-25">
                <label htmlFor="name">Name</label>
            </div>
            <div className="col-75">
                <input type="text" placeholder="Enter Name"  refs="name" onChange={this.handleChange.bind(this, "name")} value={this.state.fields["name"]}/>
                <span style={{color: "red"}}>{this.state.errors["name"]}</span>
            </div>
          </div>
          <div className="row">
            <div className="col-25">
              <label htmlFor="exampleInputEmail1">Email address</label>
            </div>
            <div className="col-75">
                <input type="email" placeholder="Enter Email" refs="email" aria-describedby="emailHelp" onChange={this.handleChange.bind(this, "email")} value={this.state.fields["email"]}/>
                <span style={{color: "red"}}>{this.state.errors["email"]}</span>
            </div>
          </div>
          <div className="row">
            <div className="col-25">
                <label htmlFor="message">Message</label>
            </div>
            <div className="col-75">
                <textarea type="text" placeholder="Enter Message" rows="5" refs="message" onChange={this.handleChange.bind(this, "message")} value={this.state.fields["message"]}></textarea>
                <span style={{color: "red"}}>{this.state.errors["message"]}</span>
            </div>
          </div>
          <div className="row">
            <button type="submit" disabled={this.state.disabled}>{this.state.disabled ? 'Sending...' : 'Send'}</button>
          </div>
      </form>
    )
  }
}

Benutzer-Avatar
Rechnung

Ich habe in der Vergangenheit Redux-Form und Formik verwendet, und kürzlich hat React Hook eingeführt, und ich habe einen benutzerdefinierten Hook dafür gebaut. Bitte überprüfen Sie es und sehen Sie, ob es Ihre Formularvalidierung viel einfacher macht.

Github: https://github.com/bluebill1049/react-hook-form

Webseite: http://react-hook-form.now.sh

Mit diesem Ansatz machen Sie auch keine kontrollierte Eingabe mehr.

Beispiel unten:

import React from 'react'
import useForm from 'react-hook-form'

function App() {
  const { register, handleSubmit, errors } = useForm() // initialise the hook
  const onSubmit = (data) => { console.log(data) } // callback when validation pass

  return (
    <form onSubmit={handleSubmit(onSubmit)}>
      <input name="firstname" ref={register} /> {/* register an input */}

      <input name="lastname" ref={register({ required: true })} /> {/* apply required validation */}
      {errors.lastname && 'Last name is required.'} {/* error message */}

      <input name="age" ref={register({ pattern: /\d+/ })} /> {/* apply a Refex validation */}
      {errors.age && 'Please enter number for age.'} {/* error message */}

      <input type="submit" />
    </form>
  )
}

Benutzer-Avatar
Przemek Strucinski

Manchmal können Sie mehrere Felder mit ähnlicher Validierung in Ihrer Anwendung haben. In einem solchen Fall empfehle ich, ein gemeinsames Komponentenfeld zu erstellen, in dem Sie diese Validierung aufbewahren.

Nehmen wir zum Beispiel an, dass Sie an einigen Stellen in Ihrer Anwendung obligatorische Texteingaben haben. Sie können eine TextInput-Komponente erstellen:

constructor(props) {
    super(props); 
    this.state = {
        touched: false, error: '', class: '', value: ''
    }
}

onValueChanged = (event) => {
    let [error, validClass, value] = ["", "", event.target.value];

    [error, validClass] = (!value && this.props.required) ? 
        ["Value cannot be empty", "is-invalid"] : ["", "is-valid"]

    this.props.onChange({value: value, error: error});

    this.setState({
        touched: true,
        error: error,
        class: validClass,
        value: value
    })
}

render() {
    return (
        <div>
            <input type="text"
                value={this.props.value}
                onChange={this.onValueChanged}
                className={"form-control " + this.state.class}
                id="{this.props.id}"
                placeholder={this.props.placeholder} />
            {this.state.error ?
                <div className="invalid-feedback">
                    {this.state.error}
                </div> : null
            }
        </div>
    )
}

Und dann können Sie eine solche Komponente überall in Ihrer Anwendung verwenden:

constructor(props) {
    super(props);
    this.state = {
        user: {firstName: '', lastName: ''},
        formState: {
            firstName: { error: '' },
            lastName: { error: '' }
        }
    }
}

onFirstNameChange = (model) => {
    let user = this.state.user;
    user.firstName = model.value;

    this.setState({
        user: user,
        formState: {...this.state.formState, firstName: { error: model.error }}
    })
}

onLastNameChange = (model) => {
    let user = this.state.user;
    user.lastName = model.value;

    this.setState({
        user: user,
        formState: {...this.state.formState, lastName: { error: model.error }}
    })
}


onSubmit = (e) => {
   // submit logic
}


render() {
    return (
        <form onSubmit={this.onSubmit}>
            <TextInput id="input_firstName"
                value={this.state.user.firstName}
                onChange={this.onFirstNameChange}
                required = {true}
                placeholder="First name" />

            <TextInput id="input_lastName"
                value={this.state.user.lastName}
                onChange={this.onLastNameChange}
                required = {true}
                placeholder="Last name" />

            {this.state.formState.firstName.error || this.state.formState.lastName.error ?
                <button type="submit" disabled className="btn btn-primary margin-left disabled">Save</button>
                : <button type="submit" className="btn btn-primary margin-left">Save</button>
            }

        </form>
    )
}

Vorteile:

  • Sie wiederholen Ihre Validierungslogik nicht
  • Weniger Code in Ihren Formularen – besser lesbar
  • Andere gemeinsame Eingangslogik kann in der Komponente gehalten werden
  • Sie folgen der React-Regel, dass die Komponente so dumm wie möglich sein sollte

Ref. https://webfellas.tech/#/article/5

1055090cookie-checkWie kann man Eingabewerte mit React.JS richtig validieren?

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy