首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >为什么在componentDidMount中设置状态需要setTimeout才能工作?

为什么在componentDidMount中设置状态需要setTimeout才能工作?
EN

Stack Overflow用户
提问于 2022-05-18 16:28:15
回答 1查看 118关注 0票数 0

我有一个如下所示的组件(是的,我知道存在React .)。我也创建了一个CodeSandbox示例。

代码语言:javascript
运行
复制
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"中使用一个元素,当它的内容发生变化时,它会宣布。它需要包括:

  • 没有什么最初的,这样,当它确实有内容,新的内容将被宣布。
  • 当组件处于加载状态时的加载指示符,该加载指示符应在组件挂载后立即发生。
  • 加载后的视觉隐藏元素已经完成,这将向屏幕阅读器用户宣布加载已经完成。

我遇到的问题是,当我在componentDidMount中将加载状态从“空闲”更改为“加载”时,它不会在DOM中“注册”,除非我将其包装在setTimeout()中,甚至只是延迟了1ms。如果我没有setTimeout(),则不会公布role="status"元素中的内容更改。

有趣的是,如果在浏览器中删除setTimeout()并打开dev工具,debugger;就会中断,当状态为“空闲”时,您可以看到UI是呈现的。这使我感到困惑,为什么有必要拖延。

需要明确的是,问题是,如果没有setTimeout(),“加载”的初始宣布就不会发生。你需要一个屏幕阅读器。)测试.

提前谢谢。

EN

Stack Overflow用户

发布于 2022-05-20 19:53:06

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

然后,在componentDidMount中调用componentDidMount实际上将触发对render的第二次调用。它读取新状态并更新DOM。

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

代码语言:javascript
运行
复制
componentDidMount() {
  this.setState({ loadingStatus: 'loading' });

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

这会导致以下流:

  • state ={ loadingStatus:‘空闲’}
  • 渲染
  • componentDidMount
  • setState({ loadingStatus:'loading‘})
  • 渲染
  • 3秒后..。setState({ loadingStatus:‘完全’})
  • 渲染

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

代码语言:javascript
运行
复制
componentDidMount() {
  this.setState({ loadingStatus: 'loading' });

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

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

代码语言:javascript
运行
复制
componentDidMount() {
  requestAnimationFrame(() => {
    this.setState({ loadingStatus: 'loading' });

    fetch('/your/api/endpoint').then(response => {
      // do something with the response
      this.setState({ loadingStatus: 'complete' });
    });
  });
}
票数 2
EN
查看全部 1 条回答
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/72292842

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档