¿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/).
¿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:
Safari 10.0.2 llama al detector de eventos change
en ambos casos.
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;
}
}
Comentario más útil
¿Está esto relacionado?
preventDefault
llamado en una casillaonChange
verificación https://codesandbox.io/s/0qpr936xkv