React: event.preventDefault im Click-Handler verhindert nicht, dass onChange aufgerufen wird

Erstellt am 17. Feb. 2017  ·  6Kommentare  ·  Quelle: facebook/react

Möchten Sie eine Funktion anfordern oder einen Fehler melden?

Fehler!

Wie ist das aktuelle Verhalten?

Beim Rendern eines input -Elements vom Typ checkbox mit einem onClick und onChange Handler wird onChange immer noch aufgerufen, obwohl event.preventDefault() wird im onClick Handler aufgerufen.

Wenn das aktuelle Verhalten ein Fehler ist, geben Sie die Schritte zum Reproduzieren und wenn möglich eine minimale Demo des Problems über https://jsfiddle.net oder ähnliches an (Vorlage: https://jsfiddle.net/reactjs/69z2wepo/).

http://jsfiddle.net/rf3w7apc/

Was ist das erwartete Verhalten?

Das Aufrufen von event.preventDefault im Handler onClick sollte verhindern, dass die Standardaktion ausgeführt wird (oder deren Wirkung rückgängig macht), dh den Wert des Elements input aktualisieren. Dies sollte verhindern, dass ein change Ereignis-Listener aufgerufen wird. Informationen zum erwarteten Verhalten finden Sie unter

Welche Versionen von React und welcher Browser / welches Betriebssystem sind von diesem Problem betroffen?

Getestet mit einem Build von Master, macOS 10.12.2, verifiziert in:

  • Chrome 56.0.2924.87 (64-Bit)
  • Firefox 51.0.1 (64-Bit)

Safari 10.0.2 ruft in beiden Fällen den Ereignis-Listener change .

DOM Bug

Hilfreichster Kommentar

Ist das verwandt? preventDefault das in einem Kontrollkästchen aufgerufen wird onChange dass React und der Browserstatus nicht mehr synchron sind: https://codesandbox.io/s/0qpr936xkv

Alle 6 Kommentare

Das change -Ereignis wird vom ChangeEventPlugin als Antwort auf topChange , bevor Ihr onClick -Handler überhaupt aufgerufen wird (also vor dem preventDefault call), hier: https://github.com/facebook/react/blob/master/src/renderers/dom/shared/eventPlugins/ChangeEventPlugin.js#L338

Dieses Plugin versucht herauszufinden, ob sich der Wert des Kontrollkästchens ändert, und wenn ja, wird das Ereignis change in die Warteschlange gestellt. Es verwendet die inputValueTracking -Funktionalität, um den vorherigen Wert mit dem nächsten Wert zu vergleichen, um diese Entscheidung zu treffen: https://github.com/facebook/react/blob/master/src/renderers/dom/shared/ inputValueTracking.js # L154

Die nextValue folgt direkt aus dem DOM entnommen: value = isCheckable(node) ? '' + node.checked : node.value; https://github.com/facebook/react/blob/master/src/renderers/dom/shared/inputValueTracking.js# L56

Das Problem ist, dass der node.checked zu diesem Zeitpunkt vorübergehend wahr ist. Nach dem ersten Klick lautet er also wie folgt:

lastValue == false (richtig)
nextValue == true (falsch)

und beim nächsten Klicken auf das Kontrollkästchen wird das Ereignis change nicht mehr ausgelöst, da die folgenden Werte vorliegen:

lastValue == true (falsch)
nextValue == true (falsch)

Insgesamt scheint das Problem zu sein, dass React das DOM nach dem Wert von checked des Knotens fragt, bevor der PreventDefault in onClick aufgerufen werden kann.

Vielen Dank an @karelskopek und @Mintaffee für Ihre Hilfe beim Eingraben.

@aweary Denkst du, das ist es wert, gelöst zu werden?

Kein Fortschritt mit diesem? Ich verwende React v16.2 und das Verhalten ist das gleiche - onChange wird beim ersten Mal ausgelöst und nachfolgende Klicks lösen es nicht aus. Ich würde hier ein Thema mit ziemlich hoher Priorität sagen.

Ist das verwandt? preventDefault das in einem Kontrollkästchen aufgerufen wird onChange dass React und der Browserstatus nicht mehr synchron sind: https://codesandbox.io/s/0qpr936xkv

Immer noch ein Problem in React 16.8.6. Eine mögliche Problemumgehung besteht darin, alles in onClick handler zu erledigen:

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`"
  }
}

Wenn Sie eine kontrollierte Komponente verwenden

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

Für eine einfachere Lösung können Sie das Ereignis e.preventDefault on onMouseDown ausführen, um zu verhindern, dass onChange ausgelöst wird.

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

War diese Seite hilfreich?
0 / 5 - 0 Bewertungen