I have a component that looks something like the below (yes, I know React hooks exist...). I've created a CodeSandbox example too.
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>
);
}
}
This component demonstrates how to make a page loading indicator that is accessible to screen reader users. The key is the use of an element with role="status"
which will announce when its content changes. It needs to contain:
- nothing initially so that, when it does have content, the new content will be announced.
- the loading indicator when the component is in a loading state which should occur immediately after the component mounts.
- a visually hidden element after loading is complete which will announce to screen reader users that loading is complete.
The issue I'm having is that when I change the loading status from "idle" to "loading" in componentDidMount, it doesn't "register" in the DOM unless I wrap it in a setTimeout(), even just a 1 ms delay. If I don't have the setTimeout(), the content change in my role="status"
element is not announced.
What's interesting is that, if you remove the setTimeout() and open up dev tools in the browser, the debugger;
breaks and you can see that the UI is rendered when the status is "idle". This has me confused why there is a need for a delay.
To be clear, the problem is that, without the setTimeout(), the initial announcing of "loading" does not occur. You'll need a screen reader (eg. NVDA) to test.
Thanks in advance.