Imagina incrementar un contador en algún componente:
class SomeComponent extends Component{
state = {
updatedByDiv: '',
updatedByBtn: '',
counter: 0
}
divCountHandler = () => {
this.setState({
updatedByDiv: 'Div',
counter: this.state.counter + 1
});
console.log('divCountHandler executed');
}
btnCountHandler = () => {
this.setState({
updatedByBtn: 'Button',
counter: this.state.counter + 1
});
console.log('btnCountHandler executed');
}
...
...
render(){
return (
...
// a parent div
<div onClick={this.divCountHandler}>
// a child button
<button onClick={this.btnCountHandler}>Increment Count</button>
</div>
...
)
}
}
Hay un controlador de recuento adjunto a los componentes primarios y secundarios. Esto se hace a propósito para que podamos ejecutar el setState () dos veces dentro del mismo contexto de evento de clic, pero desde 2 controladores diferentes.
Como podríamos imaginar, un evento de un solo clic en el botón ahora activaría estos dos controladores ya que el evento burbujea desde el objetivo al contenedor más externo durante la fase de burbujeo.
Por lo tanto, btnCountHandler () se ejecuta primero, se espera que incremente la cuenta a 1 y luego se ejecuta divCountHandler (), que se espera que incremente la cuenta a 2.
Sin embargo, el recuento solo aumenta a 1, como puede inspeccionar en las herramientas React Developer.
Esto prueba que reaccionan
pone en cola todas las llamadas setState
vuelve a esta cola después de ejecutar el último método en el contexto (el divCountHandler en este caso)
combina todas las mutaciones de objetos que ocurren dentro de múltiples llamadas setState en el mismo contexto (todas las llamadas a métodos dentro de una sola fase de evento es el mismo contexto, por ejemplo) en una sintaxis de mutación de un solo objeto (la fusión tiene sentido porque es por eso que podemos actualizar las propiedades de estado de forma independiente en setState () en primer lugar)
y lo pasa a un solo setState () para evitar que se vuelva a generar debido a múltiples llamadas setState () (esta es una descripción muy primitiva del procesamiento por lotes).
Código resultante ejecutado por react:
this.setState({
updatedByDiv: 'Div',
updatedByBtn: 'Button',
counter: this.state.counter + 1
})
Para detener este comportamiento, en lugar de pasar objetos como argumentos al método setState, se pasan devoluciones de llamada.
divCountHandler = () => {
this.setState((prevState, props) => {
return {
updatedByDiv: 'Div',
counter: prevState.counter + 1
};
});
console.log('divCountHandler executed');
}
btnCountHandler = () => {
this.setState((prevState, props) => {
return {
updatedByBtn: 'Button',
counter: prevState.counter + 1
};
});
console.log('btnCountHandler executed');
}
Después de que el último método finaliza la ejecución y cuando reacciona vuelve a procesar la cola setState, simplemente llama a la devolución de llamada para cada setState en cola, pasando el estado del componente anterior.
Esta forma de reaccionar asegura que la última devolución de llamada en la cola actualice el estado en el que todas sus contrapartes anteriores se han puesto manos a la obra.