Tengo un componente que se parece al siguiente (sí, sé que existen ganchos React...). También he creado un ejemplo de CodeSandbox .
export class App extends React.Component { state = { loadingStatus: "idle" }; componentDidMount() { debugger; setTimeout(() => { this.setState({ loadingStatus: "loading" }); }, 1); setInterval(() => { const loadingStatus = this.state.loadingStatus === "loading" ? "complete" : "loading"; this.setState({ loadingStatus }); }, 3000); } render() { const { loadingStatus } = this.state; return ( <div> <div>loadingStatus: {this.state.loadingStatus}</div> <div role="status"> {loadingStatus === "loading" && ( <img alt="loading" src="https://upload.wikimedia.org/wikipedia/commons/b/b1/Loading_icon.gif?20151024034921" /> )} {loadingStatus === "complete" && ( <span className="visually-hidden">Loading has completed</span> )} </div> {loadingStatus === "complete" && ( <div>Content has loaded. The page will reload again momentarily.</div> )} </div> ); } } Este componente demuestra cómo crear un indicador de carga de página que sea accesible para los usuarios de lectores de pantalla. La clave es el uso de un elemento con role="status" que anunciará cuando cambie su contenido. Necesita contener:
El problema que tengo es que cuando cambio el estado de carga de "inactivo" a "cargando" en el componente DidMount, no se "registra" en el DOM a menos que lo ajuste en un setTimeout (), incluso solo 1 ms demora. Si no tengo setTimeout(), no se anuncia el cambio de contenido en mi elemento role="status" .
Lo interesante es que, si elimina setTimeout() y abre las herramientas de desarrollo en el navegador, el debugger; se rompe y puede ver que la interfaz de usuario se representa cuando el estado es "inactivo". Esto me tiene confundido por qué es necesario un retraso.
Para ser claros, el problema es que, sin setTimeout(), el anuncio inicial de "carga" no ocurre. Necesitará un lector de pantalla (por ejemplo, NVDA) para probar.
Gracias por adelantado.
Se llama al método componentDidMount después de que se haya renderizado el componente. Es por eso que puede verlo cuando agrega la declaración del depurador.
Llamar setState dentro de componentDidMount activará una segunda llamada para render . Que lee el nuevo estado y actualiza el DOM.
No estoy seguro de por qué estaba usando setInterval pero si simplifica su ejemplo, en realidad funciona de la manera que esperaría.
componentDidMount() { this.setState({ loadingStatus: 'loading' }); setTimeout(() => { this.setState({ loadingStatus: 'complete' }); }, 3000); }Lo que provoca el siguiente flujo:
En el ejemplo anterior setTimeout(..., 3000) representa una llamada API simulada. Lo más probable es que desee realizar una operación asincrónica como esta:
componentDidMount() { this.setState({ loadingStatus: 'loading' }); fetch('/your/api/endpoint').then(response => { // do something with the response this.setState({ loadingStatus: 'complete' }); }); } Si aún tiene problemas con él después de probar el método anterior, podría deberse a que setState es asíncrono y los cambios que realice se pueden procesar por lotes si ocurren en un flujo de ejecución continuo. Envolver su código en una devolución de llamada de requestAnimationFrame garantizará que el renderizado anterior esté completo antes de actualizar el estado.
componentDidMount() { requestAnimationFrame(() => { this.setState({ loadingStatus: 'loading' }); fetch('/your/api/endpoint').then(response => { // do something with the response this.setState({ loadingStatus: 'complete' }); }); }); }