React: event.preventDefault en el controlador de clics no evita que se llame a onChange

Creado en 17 feb. 2017  ·  6Comentarios  ·  Fuente: facebook/react

¿Quieres solicitar una función o informar de un error ?

¡Insecto!

¿Cuál es el comportamiento actual?

Al representar un elemento input de tipo checkbox con un onClick y onChange handler, onChange todavía se llama aunque event.preventDefault() se llama en el controlador onClick .

Si el comportamiento actual es un error, proporcione los pasos para reproducir y, si es posible, una demostración mínima del problema a través de https://jsfiddle.net o similar (plantilla: https://jsfiddle.net/reactjs/69z2wepo/).

http://jsfiddle.net/rf3w7apc/

¿Cuál es el comportamiento esperado?

Llamar a event.preventDefault en el controlador onClick debería evitar que ocurra la acción predeterminada (o deshacer su efecto), que es actualizar el valor del elemento input . Esto debería evitar que se invoque cualquier detector de eventos change . Consulte https://jsfiddle.net/L1eskzsq/ para conocer el comportamiento esperado

¿Qué versiones de React y qué navegador / sistema operativo se ven afectados por este problema?

Probado con una compilación del maestro, macOS 10.12.2, verificado en:

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

Safari 10.0.2 llama al detector de eventos change en ambos casos.

DOM Bug

Comentario más útil

¿Está esto relacionado? preventDefault llamado en una casilla onChange verificación https://codesandbox.io/s/0qpr936xkv

Todos 6 comentarios

El evento change está siendo creado por el ChangeEventPlugin como respuesta a topChange , antes de que se llame a su controlador onClick (por lo tanto, antes del preventDefault call), aquí: https://github.com/facebook/react/blob/master/src/renderers/dom/shared/eventPlugins/ChangeEventPlugin.js#L338

Este complemento está tratando de averiguar si el valor de la casilla de verificación cambiará y, en caso afirmativo, pondrá en cola el evento change . Utiliza la funcionalidad inputValueTracking para comparar el valor anterior con el siguiente valor para tomar esta decisión, aquí: https://github.com/facebook/react/blob/master/src/renderers/dom/shared/ inputValueTracking.js # L154

El nextValue se toma directamente del DOM así: value = isCheckable(node) ? '' + node.checked : node.value; https://github.com/facebook/react/blob/master/src/renderers/dom/shared/inputValueTracking.js# L56

El problema es que el node.checked es temporalmente verdadero en este punto, por lo que después del primer clic es:

lastValue == false (correcto)
nextValue == true (incorrecto)

y en el siguiente clic en la casilla de verificación, el evento change ya no se activa, porque los valores son:

lastValue == true (incorrecto)
nextValue == true (incorrecto)

En general, el problema parece ser que React está preguntando al DOM cuál es el checked del nodo, antes de que se pueda llamar a preventDefault en onClick .

Gracias @karelskopek y @Mintaffee por su ayuda para

@aweary ¿Crees que vale la pena resolver esto?

¿No progresaste con este? Estoy usando React v16.2 y el comportamiento es el mismo: onChange se activa la primera vez y los clics posteriores no lo activan. Yo diría que un tema de alta prioridad aquí.

¿Está esto relacionado? preventDefault llamado en una casilla onChange verificación https://codesandbox.io/s/0qpr936xkv

Sigue siendo un problema en React 16.8.6. Una posible solución es hacer todo en onClick handler:

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 usa un componente controlado, otra opción que encontré es simplemente dejar el valor en el estado sin cambios:

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

Para una solución más simple, puede hacer e.preventDefault en el evento onMouseDown para evitar que onChange se active.

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

¿Fue útil esta página
0 / 5 - 0 calificaciones