使用异步 componentDidMount() 好吗?

新手上路,请多包涵

在 React Native 中使用 componentDidMount() 作为异步函数的良好做法还是应该避免使用它?

当组件安装时,我需要从 AsyncStorage 获取一些信息,但我知道使这成为可能的唯一方法是使 componentDidMount() 函数异步。

 async componentDidMount() {
    let auth = await this.getAuth();
    if (auth)
        this.checkAuth(auth);
}

这有什么问题吗?还有其他解决方案吗?

原文由 Mirakurun 发布,翻译遵循 CC BY-SA 4.0 许可协议

阅读 1k
2 个回答

让我们首先指出差异并确定它如何导致麻烦。

这是async和“sync”的代码 componentDidMount() 生命周期方法:

 // This is typescript code
componentDidMount(): void { /* do something */ }

async componentDidMount(): Promise<void> {
    /* do something */
    /* You can use "await" here */
}

通过查看代码,我可以指出以下差异:

  1. async 关键字:在打字稿中,这只是一个代码标记。它做了两件事:

    • 强制返回类型为 Promise<void> 而不是 void 。如果您明确指定返回类型为非承诺(例如:void),打字稿将向您吐出一个错误。
    • 允许您在方法内使用 await 关键字。
  2. 返回类型从 void 更改为 Promise<void>

    • 这意味着您现在可以这样做:

    async someMethod(): Promise<void> { await componentDidMount(); }

  3. 您现在可以在方法中使用 await 关键字并暂时暂停其执行。像这样:

    async componentDidMount(): Promise<void> {
       const users = await axios.get<string>("http://localhost:9001/users");
       const questions = await axios.get<string>("http://localhost:9001/questions");

       // Sleep for 10 seconds
       await new Promise(resolve => { setTimeout(resolve, 10000); });

       // This line of code will be executed after 10+ seconds
       this.setState({users, questions});
       return Promise.resolve();
   }

现在,他们怎么可能惹麻烦?

  1. async 关键字绝对无害。
  2. 我无法想象在任何情况下您需要调用 componentDidMount() 方法,因此返回类型 Promise<void> 也是无害的。

调用返回类型为 Promise<void> 的方法没有 await 关键字与调用返回类型为 void 的方法没有区别。

  1. 由于 componentDidMount() 之后没有生命周期方法,因此延迟其执行似乎很安全。但是有一个问题。

假设上面的 this.setState({users, questions}); 将在 10 秒后执行。在延迟时间的中间,另一个……

this.setState({users: newerUsers, questions: newerQuestions});

... 已成功执行并且 DOM 已更新。结果对用户可见。时钟继续滴答作响,10秒过去了。延迟的 `this.setState(...)` 然后将执行,并且 DOM 将再次更新,此时旧用户和旧问题。用户也可以看到结果。

=> 使用 asynccomponentDidMount() 方法非常安全(我不确定 100%)。我是它的忠实粉丝,到目前为止,我还没有遇到任何让我头疼的问题。

原文由 Cù Đức Hiếu 发布,翻译遵循 CC BY-SA 4.0 许可协议

2020 年 4 月更新: 该问题似乎已在最新的 React 16.13.1 中得到修复,请参阅 此沙箱示例。感谢@abernier 指出这一点。


我做了一些研究,发现了一个重要的区别: React 不处理来自异步生命周期方法的错误。

所以,如果你写这样的东西:

 componentDidMount()
{
    throw new Error('I crashed!');
}

那么你的错误将被 错误边界 捕获,你可以处理它并显示一个优雅的消息。

如果我们像这样更改代码:

 async componentDidMount()
{
    throw new Error('I crashed!');
}

这相当于:

 componentDidMount()
{
    return Promise.reject(new Error('I crashed!'));
}

那么 你的错误将被无声地吞噬。对你感到羞耻,反应……

那么,我们如何处理错误呢?唯一的方法似乎是像这样显式捕获:

 async componentDidMount()
{
    try
    {
         await myAsyncFunction();
    }
    catch(error)
    {
        //...
    }
}

或者像这样:

 componentDidMount()
{
    myAsyncFunction()
    .catch(()=>
    {
        //...
    });
}

如果我们仍然希望我们的错误到达错误边界,我可以考虑以下技巧:

  1. 捕获错误,使错误处理程序更改组件状态
  2. 如果状态指示错误,则从 render 方法中抛出

例子:

 class BuggyComponent extends React.Component {
  constructor(props) {
    super(props);
    this.state = { error: null };
  }

  buggyAsyncfunction() { return Promise.reject(new Error('I crashed async!'));}

  async componentDidMount() {
    try
    {
      await this.buggyAsyncfunction();
    }
    catch(error)
    {
        this.setState({error: error});
    }
  }

  render() {
    if(this.state.error)
        throw this.state.error;

    return <h1>I am OK</h1>;
  }
}

原文由 C-F 发布,翻译遵循 CC BY-SA 4.0 许可协议

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
logo
Stack Overflow 翻译
子站问答
访问
宣传栏