17 votes

Les champs de formulaire perdent le focus lorsque la valeur de l'entrée change

J'essaie de construire un formulaire avec des champs conditionnels à partir d'un schéma JSON en utilisant react-jsonschema-form y react-jsonschem-formalités conditionnelles .

Les composants que je rends sont un FormWithConditionals et un FormModelInspector . Ce dernier est un composant très simple qui montre le modèle de formulaire.

screen shot 2018-02-01 at 17 50 32

Le code source correspondant est le suivant :

import React from 'react';
import PropTypes from 'prop-types';
import Engine from "json-rules-engine-simplified";
import Form from "react-jsonschema-form";
import applyRules from "react-jsonschema-form-conditionals";

function FormModelInspector (props) {

  return (
    <div>
      <div className="checkbox">
        <label>
          <input type="checkbox" onChange={props.onChange} checked={props.showModel}/>
          Show Form Model
        </label>
      </div>
      {
        props.showModel && <pre>{JSON.stringify(props.formData, null, 2)}</pre>
      }
    </div>
  )
}

class ConditionalForm extends React.Component {

  constructor (props) {
    super(props);
    this.state = {
      formData: {},
      showModel: true
    };
    this.handleFormDataChange = this.handleFormDataChange.bind(this);
    this.handleShowModelChange = this.handleShowModelChange.bind(this);
  }

  handleShowModelChange (event) {
    this.setState({showModel: event.target.checked});
  }

  handleFormDataChange ({formData}) {
    this.setState({formData});
  }

  render () {
    const schema = {
      type: "object",
      title: "User form",
      properties: {
        nameHider: {
          type: 'boolean',
          title: 'Hide name'
        },
        name: {
          type: 'string',
          title: 'Name'
        }
      }
    };

    const uiSchema = {};

    const rules = [{
      conditions: {
        nameHider: {is: true}
      },
      event: {
        type: "remove",
        params: {
          field: "name"
        }
      }
    }];

    const FormWithConditionals = applyRules(schema, uiSchema, rules, Engine)(Form);

    return (
      <div className="row">
        <div className="col-md-6">
          <FormWithConditionals schema={schema}
                uiSchema={uiSchema}
                formData={this.state.formData}
                onChange={this.handleFormDataChange}
                noHtml5Validate={true}>
          </FormWithConditionals>
        </div>
        <div className="col-md-6">
          <FormModelInspector formData={this.state.formData}
                              showModel={this.state.showModel}
                              onChange={this.handleShowModelChange}/>
        </div>
      </div>
    );
  }
}

ConditionalForm.propTypes = {
  schema: PropTypes.object.isRequired,
  uiSchema: PropTypes.object.isRequired,
  rules: PropTypes.array.isRequired
};

ConditionalForm.defaultProps = {
  uiSchema: {},
  rules: []
};

Cependant, chaque fois que je modifie la valeur d'un champ, celui-ci perd le focus. Je soupçonne que la cause du problème est quelque chose dans le fichier react-jsonschema-form-conditionals car si je remplace <FormWithConditionals> con <Form> le problème ne se pose pas.

Si je supprime le gestionnaire onChange={this.handleFormDataChange} le champ de saisie ne perd plus le focus lorsque sa valeur change (mais la suppression de ce gestionnaire interrompt le fonctionnement de l'application FormModelInspector ).

A l'écart

Dans le code ci-dessus, si je supprime le gestionnaire onChange={this.handleFormDataChange} , le <FormModelInspector> n'est pas mis à jour lorsque les données du formulaire sont modifiées. Je ne comprends pas pourquoi ce gestionnaire est nécessaire car la fonction <FormModelInspector> reçoit une référence aux données du formulaire par l'intermédiaire de la fonction formData attribut. Peut-être est-ce dû au fait que chaque modification des données du formulaire entraîne la construction d'un nouvel objet, plutôt qu'une modification du même objet ?

10voto

Shubham Khatri Points 67350

Le problème est assez simple : vous créez un fichier FormWithConditionals dans votre méthode de rendu et dans votre onChange manipulateur vous setState ce qui déclenche un nouveau rendu et donc une nouvelle instance de FormWithConditionals est créée et perd ainsi de son intérêt. Vous devez déplacer cette instance hors de la méthode de rendu et peut-être hors du composant lui-même puisqu'il utilise des valeurs statiques.

En tant que schema , uiSchema y rules sont transmises en tant qu'accessoires à l'outil ConditionalForm vous pouvez créer une instance de FormWithConditionals en constructor et l'utiliser dans le rendu comme ceci

    import React from 'react';
    import PropTypes from 'prop-types';
    import Engine from "json-rules-engine-simplified";
    import Form from "react-jsonschema-form";
    import applyRules from "react-jsonschema-form-conditionals";

    function FormModelInspector (props) {

      return (
        <div>
          <div className="checkbox">
            <label>
              <input type="checkbox" onChange={props.onChange} checked={props.showModel}/>
              Show Form Model
            </label>
          </div>
          {
            props.showModel && <pre>{JSON.stringify(props.formData, null, 2)}</pre>
          }
        </div>
      )
    }

    class ConditionalForm extends React.Component {

      constructor (props) {
        super(props);
        this.state = {
          formData: {},
          showModel: true
        };
        const { schema, uiSchema, rules } = props;
        this.FormWithConditionals = applyRules(schema, uiSchema, rules, Engine)(Form);
        this.handleFormDataChange = this.handleFormDataChange.bind(this);
        this.handleShowModelChange = this.handleShowModelChange.bind(this);
      }

      handleShowModelChange (event) {
        this.setState({showModel: event.target.checked});
      }

      handleFormDataChange ({formData}) {
        this.setState({formData});
      }

      render () {
        const FormWithConditionals = this.FormWithConditionals;
        return (
          <div className="row">
            <div className="col-md-6">
              <FormWithConditionals schema={schema}
                    uiSchema={uiSchema}
                    formData={this.state.formData}
                    onChange={this.handleFormDataChange}
                    noHtml5Validate={true}>
              </FormWithConditionals>
            </div>
            <div className="col-md-6">
              <FormModelInspector formData={this.state.formData}
                                  showModel={this.state.showModel}
                                  onChange={this.handleShowModelChange}/>
            </div>
          </div>
        );
      }
    }

    ConditionalForm.propTypes = {
      schema: PropTypes.object.isRequired,
      uiSchema: PropTypes.object.isRequired,
      rules: PropTypes.array.isRequired
    };

    ConditionalForm.defaultProps = {
      uiSchema: {},
      rules: []
    };

0voto

Eric Martin Points 567

Pour ceux qui rencontrent le même problème mais qui utilisent des Hooks, voici comment faire sans classe :

Il suffit d'utiliser une variable déclarée à l'extérieur du composant et de l'initialiser à l'intérieur. useEffect . (n'oubliez pas de passer [] comme second paramètre pour indiquer à react que nous ne dépendons d'aucune variable, reproduisant ainsi la méthode de la componentWillMount effet)

// import ...
import Engine from 'json-rules-engine-simplified'
import Form from 'react-jsonschema-form'

let FormWithConditionals = () => null

const MyComponent = (props) => {
  const {
    formData,
    schema,
    uischema,
    rules,
  } = props;

  useEffect(() => {
    FormWithConditionals = applyRules(schema, uischema, rules, Engine)(Form)
  }, [])

  return (
    <FormWithConditionals>
      <div></div>
    </FormWithConditionals>
  );
}

export default MyComponent

-1voto

Dyo Points 3229

Avez-vous essayé de déclarer function FormModelInspector comme une flèche func :

const FormModelInspector = props => (
    <div>
      <div className="checkbox">
        <label>
          <input type="checkbox" onChange={props.onChange} checked={props.showModel}/>
          Show Form Model
        </label>
      </div>
      {
        props.showModel && <pre>{JSON.stringify(props.formData, null, 2)}</pre>
      }
    </div>
  )

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X