javascript - ¿Qué sucede al usar this.setState varias veces en un componente de React?
Quería comprobar qué sucede cuando usas this.setState varias veces (2 veces para el propósito de la discusión). Pensé que el componente se renderizaría dos veces, pero aparentemente se renderiza solo una vez. Otra expectativa que tenía era que tal vez la segunda llamada a setState sobrescribiera la primera, pero lo adivinaste: funcionó bien.
Enlace a un JSfiddle
var Hello = React.createClass({
render: function() {
return (
<div>
<div>Hello {this.props.name}</div>
<CheckBox />
</div>
);
}
});
var CheckBox = React.createClass({
getInitialState: function() {
return {
alex: 0
};
},
handleChange: function(event) {
this.setState({
value: event.target.value
});
this.setState({
alex: 5
});
},
render: function() {
alert('render');
return (
<div>
<label htmlFor="alex">Alex</label>
<input type="checkbox" onChange={this.handleChange} name="alex" />
<div>{this.state.alex}</div>
</div>
);
}
});
ReactDOM.render(
<Hello name="World" />,
document.getElementById('container')
);
Como verás, aparece una alerta que dice 'render' en cada renderizado.
¿Tienes una explicación de por qué funcionó correctamente?
Respuesta #1
React agrupa las actualizaciones de estado que ocurren en los manejadores de eventos y métodos del ciclo de vida. Por lo tanto, si actualizas el estado varias veces en un manejador <div onClick />, React esperará a que termine el manejo del evento antes de volver a renderizar.
Para ser claro, esto solo funciona en los controladores de eventos sintéticos controlados por React y en los métodos del ciclo de vida. Las actualizaciones de estado no se agrupan en los controladores de eventos de AJAX y setTimeout, por ejemplo.
ACTUALIZACIÓN
Con el lanzamiento de React v18.0, las actualizaciones de estado dentro de eventos no relacionados con React (promesas, setTimeout, etc.) también se agrupan por defecto.
Ref - https://reactjs.org/blog/2022/03/29/react-v18.html#new-feature-automatic-batching
Respuesta #2
El método setState() no actualiza inmediatamente el estado del componente, solo coloca la actualización en una cola para ser procesada más tarde. React puede agrupar múltiples solicitudes de actualización para hacer que el renderizado sea más eficiente. Debido a esto, se deben tomar precauciones especiales cuando se intenta actualizar el estado basado en el estado previo del componente.
Por ejemplo, el siguiente código solo incrementará el atributo del valor del estado en 1 aunque se haya llamado 4 veces:
class Counter extends React.Component{
constructor(props){
super(props)
//initial state set up
this.state = {value:0}
}
componentDidMount(){
//updating state
this.setState({value:this.state.value+1})
this.setState({value:this.state.value+1})
this.setState({value:this.state.value+1})
this.setState({value:this.state.value+1})
}
render(){
return <div>Message:{this.state.value}</div>
}
}
Para usar un estado después de que se haya actualizado, realiza toda la lógica en el argumento de devolución de llamada:
//this.state.count is originally 0
this.setState({count:42}, () => {
console.log(this.state.count)
//outputs 42
})
El método setState(updater,[callback]) puede recibir una función actualizadora como su primer argumento para actualizar el estado basado en el estado y las propiedades previas. El valor devuelto por la función actualizadora se combinará de manera superficial con el estado previo del componente. El método actualiza el estado de manera asincrónica, por lo que existe una opción de callback que se llamará una vez que el estado haya terminado de actualizarse completamente.
Ejemplo :
this.setState((prevState, props) => {
return {attribute:"value"}
})
Aquí hay un ejemplo de cómo actualizar el estado basado en el estado anterior:
class Counter extends React.Component{
constructor(props) {
super(props)
//initial state set up
this.state = {message:"initial message"}
}
componentDidMount() {
//updating state
this.setState((prevState, props) => {
return {message: prevState.message + '!'}
})
}
render(){
return <div>Message:{this.state.message}</div>
}
}
