为什么在componentDidMount设置状态需要setTimeout工作?



我有一个看起来像下面的组件(是的,我知道React钩子存在…)。我也创建了一个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>
);
}
}

这个组件演示了如何制作一个屏幕阅读器用户可以访问的页面加载指示器。关键是使用具有role="status"的元素,该元素将在其内容更改时宣布。它需要包含:

  • 最初没有内容,以便当它有内容时,新的内容将被宣布。
  • 组件处于加载状态时的加载指示器,加载状态应该在组件安装后立即发生。
  • 加载完成后,一个视觉上隐藏的元素,它将向屏幕阅读器用户宣布加载完成。

我遇到的问题是,当我改变加载状态从"idle";loading"在componentDidMount中,它不会"注册"。除非我在setTimeout()中包装它,即使只有1毫秒的延迟。如果没有setTimeout(),则不会宣布role="status"元素中的内容更改。

有趣的是,如果你删除setTimeout()并在浏览器中打开开发工具,debugger;中断,你可以看到UI呈现时的状态是&;idle&;。这使我很困惑,为什么有必要延期。

需要明确的是,问题在于,如果没有setTimeout(),初始声明的"loading"不会发生。你需要一个屏幕阅读器(例如。

提前感谢。

componentDidMount方法在之后称为组件已经呈现。这就是为什么在添加调试器语句时可以看到它的原因。

componentDidMount内部调用setState实际上会触发对render的第二次调用。它读取新的状态并更新DOM。

我不知道你为什么使用setInterval,但如果你简化你的例子,它实际上是你所期望的方式。

componentDidMount() {
this.setState({ loadingStatus: 'loading' });
setTimeout(() => {
this.setState({ loadingStatus: 'complete' });
}, 3000);
}

导致以下流程:

  • state = {loadingStatus: 'idle'}
  • componentDidMount
  • setState({loadingStatus: 'loading'})
  • 3秒后…setState({loadingStatus: 'complete'})

在上面的示例中,setTimeout(..., 3000)表示模拟的API调用。您很可能希望执行如下异步操作:

componentDidMount() {
this.setState({ loadingStatus: 'loading' });
fetch('/your/api/endpoint').then(response => {
// do something with the response
this.setState({ loadingStatus: 'complete' });
});
}

如果你在尝试上述方法后仍然遇到问题,可能是因为setState是异步的,如果它们发生在一个连续的执行流中,你所做的更改可以批处理。在requestAnimationFrame回调中包装代码将确保在更新状态之前完成先前的渲染。

componentDidMount() {
requestAnimationFrame(() => {
this.setState({ loadingStatus: 'loading' });
fetch('/your/api/endpoint').then(response => {
// do something with the response
this.setState({ loadingStatus: 'complete' });
});
});
}

最新更新