Yo trabajo en React.
TLDR:
Pero, ¿puede confiar en React para actualizar el estado en el mismo orden en que se solicita setState?
Si.
Si.
El orden de las actualizaciones siempre se respeta. Si ves un estado intermedio "entre" o no depende de si estás dentro de un lote o no.
Actualmente (React 16 y anteriores), solo las actualizaciones dentro de los controladores de eventos React se agrupan por defecto . Hay una API inestable para forzar el procesamiento por lotes fuera de los controladores de eventos en casos excepcionales cuando lo necesita.
En futuras versiones (probablemente React 17 y posteriores), React agrupará todas las actualizaciones por defecto para que no tenga que pensar en esto. Como siempre, anunciaremos cualquier cambio al respecto en el blog React y en las notas de la versión.
La clave para entender esto es que no importa cuántas setState()
llamadas en cuántos componentes realice dentro de un controlador de eventos React , producirán solo una nueva representación al final del evento . Esto es crucial para un buen rendimiento en aplicaciones grandes porque si Child
y Parent
cada llamada setState()
al manejar un evento de clic, no desea volver a renderizar el Child
doble.
En ambos ejemplos, las setState()
llamadas ocurren dentro de un controlador de eventos React. Por lo tanto, siempre se enjuagan al final del evento (y no se ve el estado intermedio).
Las actualizaciones siempre se fusionan superficialmente en el orden en que ocurren . Entonces, si la primera actualización es {a: 10}
, la segunda es {b: 20}
, y la tercera es {a: 30}
, el estado representado será {a: 30, b: 20}
. La actualización más reciente de la misma clave de estado (por ejemplo, como a
en mi ejemplo) siempre "gana".
El this.state
objeto se actualiza cuando volvemos a representar la interfaz de usuario al final del lote. Entonces, si necesita actualizar el estado en función de un estado anterior (como incrementar un contador), debe usar la setState(fn)
versión funcional que le proporciona el estado anterior, en lugar de leer this.state
. Si tienes curiosidad sobre el razonamiento de esto, lo expliqué en profundidad en este comentario .
En su ejemplo, no veríamos el "estado intermedio" porque estamos dentro de un controlador de eventos React donde se habilita el procesamiento por lotes (porque React "sabe" cuando salimos de ese evento).
Sin embargo, tanto en React 16 como en versiones anteriores, todavía no hay lotes por defecto fuera de los controladores de eventos React . Entonces, si en su ejemplo tuviéramos un controlador de respuesta AJAX en lugar de handleClick
, cada uno setState()
se procesaría de inmediato cuando ocurriera. En este caso, sí, usted podría ver un estado intermedio:
promise.then(() => {
// We're not in an event handler, so these are flushed separately.
this.setState({a: true}); // Re-renders with {a: true, b: false }
this.setState({b: true}); // Re-renders with {a: true, b: true }
this.props.setParentState(); // Re-renders the parent
});
Sabemos que es inconveniente que el comportamiento sea diferente dependiendo de si estás en un controlador de eventos o no . Esto cambiará en una futura versión de React que agrupará todas las actualizaciones por defecto (y proporcionará una API opcional para eliminar los cambios sincrónicamente). Hasta que cambiemos el comportamiento predeterminado (potencialmente en React 17), hay una API que puede usar para forzar el procesamiento por lotes :
promise.then(() => {
// Forces batching
ReactDOM.unstable_batchedUpdates(() => {
this.setState({a: true}); // Doesn't re-render yet
this.setState({b: true}); // Doesn't re-render yet
this.props.setParentState(); // Doesn't re-render yet
});
// When we exit unstable_batchedUpdates, re-renders once
});
Los controladores de eventos de Reaccionar internamente se están envolviendo, por unstable_batchedUpdates
lo que están agrupados por defecto. Tenga en cuenta que ajustar una actualización unstable_batchedUpdates
dos veces no tiene ningún efecto. Las actualizaciones se vacían cuando salimos de la unstable_batchedUpdates
llamada más externa .
Esa API es "inestable" en el sentido de que la eliminaremos cuando el procesamiento por lotes ya esté habilitado de forma predeterminada. Sin embargo, no lo eliminaremos en una versión menor, por lo que puede confiar en él de forma segura hasta React 17 si necesita forzar el procesamiento por lotes en algunos casos fuera de los controladores de eventos React.
En resumen, este es un tema confuso porque React solo se agrupa dentro de los controladores de eventos de forma predeterminada. Esto cambiará en futuras versiones, y el comportamiento será más directo que entonces. Pero la solución no es agrupar menos , sino agrupar más por defecto. Eso es lo que vamos a hacer.