使用异步componentDidMount()好吗?

Mir*_*run 101 asynchronous reactjs react-native

使用componentDidMount()作为一个异步函数的良好做法作出反应本地还是应该避免呢?

我需要从AsyncStorage组件安装时获取一些信息,但我知道实现这一点的唯一方法是使componentDidMount()函数异步.

async componentDidMount() {
    let auth = await this.getAuth();
    if (auth) 
        this.checkAuth(auth);
}
Run Code Online (Sandbox Code Playgroud)

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

Cù *_*iếu 125

让我们首先指出差异并确定它如何引发麻烦.

以下是异步和"同步" componentDidMount()生命周期方法的代码:

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

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

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

  1. async关键字:在打字稿,这仅仅是一个代码标记.它做了两件事:
    • 强制返回类型Promise<void>而不是void.如果您明确指定返回类型为非承诺(例如:void),则typescript将向您发出错误.
    • 允许您await在方法中使用关键字.
  2. 返回类型从更改voidPromise<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();
    }
    
    Run Code Online (Sandbox Code Playgroud)

现在,他们怎么会引起麻烦?

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

    调用返回类型为Promise<void>without awaitkeyword 的方法与调用具有返回类型的方法没有区别void.

  3. 由于componentDidMount()延迟执行后没有生命周期方法似乎非常安全.但是有一个问题.

    比方说,以上this.setState({users, questions});将在10秒后执行.在延迟时间的中间,另一个......

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

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

=>这是很安全的(我不知道约100%),使用asynccomponentDidMount()方法.我是它的忠实粉丝,到目前为止我还没有遇到任何让我头疼的问题.

  • 是! 延迟`setState()`总是有很小的风险.我们应该小心谨慎. (2认同)
  • 如果在卸载组件后执行setState,可能会遇到问题 (2认同)

Tia*_*ves 8

你的代码很好,对我来说非常易读.看看这个Dale Jefferson的文章,他在那里展示了一个异步的componentDidMount例子,看起来也非常好.

但有些人会说,阅读代码的人可能会认为React对返回的承诺做了一些事情.

因此,对此代码的解释以及是否是一种良好的实践是非常个人化的.

如果您想要其他解决方案,可以使用promises.例如:

componentDidMount() {
    fetch(this.getAuth())
      .then(auth => {
          if (auth) this.checkAuth(auth)
      })
}
Run Code Online (Sandbox Code Playgroud)

  • ...或者,也可以使用`await`s inside ...内联`async`函数? (3认同)

Lu *_*ran 7

当您使用componentDidMountwithoutasync关键字时,文档会这样说:

您可以在 componentDidMount() 中立即调用 setState()。它会触发额外的渲染,但它会在浏览器更新屏幕之前发生。

如果你使用async componentDidMount你会失去这个能力:在浏览器更新屏幕后会发生另一个渲染。但是imo,如果您正在考虑使用异步,例如获取数据,则无法避免浏览器将更新屏幕两次。在另一个世界,在浏览器更新屏幕之前暂停 componentDidMount 是不可能的


C-F*_*C-F 5

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

因此,如果您编写如下内容:

componentDidMount()
{
    throw new Error('I crashed!');
}
Run Code Online (Sandbox Code Playgroud)

那么您的错误将被error boundry捕获,并且您可以对其进行处理并显示优美的消息。

如果我们这样更改代码:

async componentDidMount()
{
    throw new Error('I crashed!');
}
Run Code Online (Sandbox Code Playgroud)

这等效于:

componentDidMount()
{
    return Promise.reject(new Error('I crashed!'));
}
Run Code Online (Sandbox Code Playgroud)

这样您的错误就会被默默吞噬。对你感到羞耻,反应...

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

async componentDidMount()
{
    try
    {
         await myAsyncFunction();
    }
    catch(error)
    {
        //...
    }
}
Run Code Online (Sandbox Code Playgroud)

或像这样:

componentDidMount()
{
    myAsyncFunction()
    .catch(()=>
    {
        //...
    });
}
Run Code Online (Sandbox Code Playgroud)

如果我们仍然希望我们的错误丰富错误边界,那么我可以考虑以下技巧:

  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>;
  }
}
Run Code Online (Sandbox Code Playgroud)

  • 实际上,@abernier,如果您关闭codesandbox中的错误窗口,您将看到页面仍然呈现。我[分叉了你的codesandbox](https://codesandbox.io/s/staging-sun-rf2bk?file=/src/App.js)并添加了一个错误边界。如果您从“componentDidMount”中删除“async”,您将看到错误边界捕获错误。如果您保留它,则不会捕获任何错误。只需确保关闭错误消息窗口即可。 (3认同)