React: event.preventDefault dans le gestionnaire de clics n'empêche pas onChange d'être appelé

Créé le 17 févr. 2017  ·  6Commentaires  ·  Source: facebook/react

Voulez-vous demander une fonctionnalité ou signaler un bogue ?

Punaise!

Quel est le comportement actuel?

Lors du rendu d'un élément input de type checkbox avec un gestionnaire onClick et onChange , onChange est toujours appelé même si event.preventDefault() est appelé dans le gestionnaire onClick .

Si le comportement actuel est un bogue, veuillez fournir les étapes pour reproduire et si possible une démo minimale du problème via https://jsfiddle.net ou similaire (template: https://jsfiddle.net/reactjs/69z2wepo/).

http://jsfiddle.net/rf3w7apc/

Quel est le comportement attendu?

L'appel de event.preventDefault dans le gestionnaire onClick devrait empêcher l'action par défaut de se produire (ou annuler son effet), qui consiste à mettre à jour la valeur de l'élément input . Cela devrait empêcher tout écouteur d'événement change d'être appelé. Voir https://jsfiddle.net/L1eskzsq/ pour le comportement attendu

Quelles versions de React et quel navigateur / système d'exploitation sont concernés par ce problème?

Testé à l'aide d'une version de master, macOS 10.12.2, vérifié dans:

  • Chrome 56.0.2924.87 (64 bits)
  • Firefox 51.0.1 (64 bits)

Safari 10.0.2 appelle l'écouteur d'événement change dans les deux cas.

DOM Bug

Commentaire le plus utile

Est-ce lié? preventDefault appelé dans une case onChange cocher https://codesandbox.io/s/0qpr936xkv

Tous les 6 commentaires

L'événement change est créé par le ChangeEventPlugin en réponse à topChange , avant même que votre gestionnaire onClick soit appelé (donc avant le preventDefault call), ici: https://github.com/facebook/react/blob/master/src/renderers/dom/shared/eventPlugins/ChangeEventPlugin.js#L338

Ce plugin essaie de déterminer si la valeur de la case à cocher changera, et si oui, il mettra en file d'attente l'événement change . Il utilise la fonctionnalité inputValueTracking pour comparer la valeur précédente avec la valeur suivante pour prendre cette décision, ici: https://github.com/facebook/react/blob/master/src/renderers/dom/shared/ inputValueTracking.js # L154

Le nextValue est pris directement du DOM comme ceci: value = isCheckable(node) ? '' + node.checked : node.value; https://github.com/facebook/react/blob/master/src/renderers/dom/shared/inputValueTracking.js# L56

Le problème est que la node.checked est temporairement vraie à ce stade, donc après le premier clic, c'est:

lastValue == false (correct)
nextValue == true (incorrect)

et au prochain clic sur la case à cocher, l'événement change n'est plus déclenché, car les valeurs sont:

lastValue == true (incorrect)
nextValue == true (incorrect)

Dans l'ensemble, le problème semble être que React demande au DOM quelle est la checked du nœud, avant que le preventDefault dans onClick puisse être appelé.

Merci @karelskopek et @Mintaffee pour votre aide dans ce domaine.

@aweary Pensez-vous que cela vaut la peine d'être résolu?

Pas de progrès avec celui-ci? J'utilise React v16.2 et le comportement est le même - onChange est déclenché la première fois et les clics suivants ne le déclenchent pas. Je dirais ici une question très prioritaire.

Est-ce lié? preventDefault appelé dans une case onChange cocher https://codesandbox.io/s/0qpr936xkv

Encore un problème dans React 16.8.6. Une solution de contournement possible consiste à tout faire dans le gestionnaire onClick :

const radios = ['one', 'two', 'three'];

class Row extends Component {
  constructor(props) {
    super(props);
    this.onClick = this.onClick.bind(this);
    this.onChange = this.onChange.bind(this);
  }

  render() {
    const { value } = this.props;
    return (
      <div className="row">
        <div className="radio-group">
          {radios.map(radio => (
            <label key={radio} className="radio">
              <input
                type="radio"
                name="radio"
                value={radio}
                checked={radio === value}
                onClick={this.onClick}
                onChange={this.onChange}
              /><span>Radio</span>
            </label>
          ))}
        </div>
      </div>
    );
  }

  onClick(event) {
    // Check something to prevent radio change
    if (...) {
      event.preventDefault();
      return;
    }
    // Sync value in the store
    this.props.setValue(event.target.value);
  }
  onChange() {
    // We need this empty handler to suppress React warning:
    // "You provided a `checked` prop to a form field without an `onChange` handler.
    // This will render a read-only field. If the field should be mutable use `defaultChecked`.
    // Otherwise, set either `onChange` or `readOnly`"
  }
}

Si vous utilisez un composant contrôlé, une autre option que j'ai trouvée est de simplement laisser la valeur dans l'état inchangé:

onChange(event) {
  // Check something to prevent radio change
  if (...) {
    return;
  }
  // Sync value in the store
  this.props.setDateTime(event.target.value);
}

Pour une solution plus simple, vous pouvez exécuter l'événement e.preventDefault sur onMouseDown pour empêcher onChange de se déclencher.

onMouseDown(e) { if (...) { e.preventDefault(); return; } }

Cette page vous a été utile?
0 / 5 - 0 notes