Acabo de encontrar un evento extraviado.preventDefault event.preventDefault()
que estaba rompiendo el controlador onChange
de mis casillas de verificación:
import { Component } from 'react'; class App extends Component { constructor(props) { super(props) this.state = { accepted: false } } changeChecked = (event) => { this.setState((state) => ({ accepted : !state.accepted })); event.preventDefault(); // <- this very bad } render() { return ( <input type="checkbox" onChange={this.changeChecked} checked={this.state.accepted} /> ); } } export default App;
El comportamiento resultante es un estado actualizado correctamente en el primer clic, pero la casilla de verificación solo cambia a su apariencia 'marcada' en la siguiente representación, por ejemplo. un segundo clic.
¿Porqué es eso? ¿No es el objetivo de los componentes controlados trabajar independientemente de los eventos del navegador?
Alguien que me explique esto definitivamente aliviaría el dolor de las horas dedicadas a reducir mi complejo caso de uso. ¡Gracias!
Actualización : aquí hay un ejemplo rápido de Codepen que demuestra el comportamiento extraño. Incluí una 'casilla de verificación no prevenida' y una con un evento onClick
prevenido como comparación. Observe cómo el que tiene onChange
prevenido cambia su apariencia a su estado real tan pronto como hago clic en una casilla de verificación diferente .
Las casillas de verificación se comportan un poco diferente. Como probablemente sepa, el caso de uso típico para preventDefault()
es la función onSubmit()
de un formulario en el que realizará su propia llamada AJAX y, por lo tanto, querrá evitar el envío del formulario predeterminado. Pero con la casilla de verificación (y la mayoría de las entradas) hay un poco más de implicación.
Según MDN , el atributo checked
es "Un atributo booleano que indica si esta casilla de verificación está marcada o no de forma predeterminada (cuando se carga la página). No indica si esta casilla de verificación está marcada actualmente: si el estado de la casilla de verificación cambia, este atributo de contenido no no reflejan el cambio". Entonces, de una manera divertida, hay una desconexión entre el atributo checked
y si el estado de la entrada está marcado o no. Cuando se trata de React, en cada renderización, el atributo checked
reflejará el estado actual, pero sigue siendo solo un valor predeterminado en el sentido de que la entrada se ha renderizado recientemente y no se ha manipulado desde la última vez que cambió el estado.
eventListener
nativo para <input type="checkbox" />
Además, sin desviarnos demasiado, el evento que cambia de forma nativa el estado en una entrada de casilla de verificación es en realidad el evento de click
, no el evento de change
. Si tuviera que meterse con los oyentes y los valores de una entrada de casilla de verificación en la consola js de su navegador, vería que podría manipularlo de manera que la casilla de verificación no esté marcada, pero los valores del nodo dicen lo contrario.
Según lo anterior, en este caso no desea evitar el comportamiento predeterminado, porque el comportamiento predeterminado en el evento de change
no entra en conflicto con lo que desea hacer (y, por alguna razón, evitarlo causa problemas). De hecho, en los ejemplos de documentos de React , notará que no usan preventDefault()
al actualizar el estado de los componentes controlados. Desearía tener una mejor comprensión de exactamente por qué agregar preventDefault()
para cambiar los controladores de entradas causa problemas como este, pero espero que estos pequeños datos brinden más claridad.
Intente mover event.preventDefault()
arriba de this.setState
.