ReactJs:如何在渲染之前等待 componentDidMount() 完成?

Posted

技术标签:

【中文标题】ReactJs:如何在渲染之前等待 componentDidMount() 完成?【英文标题】:ReactJs: How to wait for componentDidMount() to finish before rendering? 【发布时间】:2018-05-02 18:05:18 【问题描述】:

如何在渲染前等待异步 componentDidMount() 完成?

我的 app.jsx:

constructor(props) 
    super(props);

    this.state = 
        loggedInUser: null,
        isAuthenticated: false,
        isAuthenticating: true
    ;


componentDidMount() 
    try 
        var user = authUser();
        console.log('User: ' + user)
        if (user) 
            console.log('Is logged in: ' + this.state.loggedInUser)
            this.userHasAuthenticated(true);  
        
    
    catch(e) 
       alert(e);
    
    this.setState( isAuthenticating: false );


render()  
   console.log('in render: ' + this.state.loggedInUser)
   // Should execute **after** authUser() in componentDidMount has finished  
   ...

componentDidMount 调用此异步函数:

function authUser() 
    firebase.auth().onAuthStateChanged(function(user) 
        return user
    )

console.log('in render: ' + this.state.loggedInUser)

如何让渲染方法等待 componentDidMount 中的 authUser()?

【问题讨论】:

【参考方案1】:

在渲染之前不要等待componentDidMount 完成,这将是对库的滥用,请等待您的authUser 完成。

您可以通过将isAuthenticating 状态属性与promises 结合使用来做到这一点。

function authUser() 
   return new Promise(function (resolve, reject) 
      firebase.auth().onAuthStateChanged(function(user) 
         if (user) 
            resolve(user);
          else 
            reject('User not logged in');
                      
      );
   );

您可以使用现有的isAuthenticating 标志,如下所示:

componentDidMount() 
    authUser().then((user) => 
       this.userHasAuthenticated(true);
       this.setState( isAuthenticating: false );
    , (error) => 
       this.setState( isAuthenticating: false );
       alert(e);
    );

然后内部渲染:

render() 
   if (this.state.isAuthenticating) return null;
   ...

这将阻止您的组件被添加到 DOM,直到您的 authUser 函数完成。

【讨论】:

谢谢,这可以解决问题。我应该阅读承诺:) 与加载为 true 时呈现的 Spinner 组件完美搭配 相当简洁的解决方案【参考方案2】:

您的authUser() 函数似乎设置不正确。您在回调中返回用户对象,但函数本身没有返回任何内容,因此 var user = authUser(); 将始终返回 undefined

您需要更改 authUser() 以调用回调函数或返回 Promise,当用户从 Firebase 返回时,该函数会解析。然后在解决承诺或执行回调后将身份验证状态设置为您的状态。如果身份验证尚未完成,请在您的 render() 函数中返回 null

带回调的异步函数:

function authUser(callback) 
    firebase.auth().onAuthStateChanged(function(user) 
        callback(user);
    )

在您的组件中使用回调:

componentDidMount() 
    try 
        authUser(function(user) 
            console.log('User: ' + user)
            if (user) 
                console.log('Is logged in: ' + this.state.loggedInUser)
                this.userHasAuthenticated(true);  
                this.setState( isAuthenticating: false );
            
        );
    
    catch(e) 
       alert(e);
    


render()  
   console.log('in render: ' + this.state.loggedInUser)
   if (this.state.isAuthenticating === true) 
       return null;
   
   // Rest of component rendering here

【讨论】:

【参考方案3】:

componentDidMount 总是会在第一次渲染之后触发。

使用componentWillMount 或使用第二个渲染,setState 触发新的渲染,componentWillMount 总是在组件安装后触发,即正确渲染。

【讨论】:

【参考方案4】:

如果您希望组件不被渲染,请使用一些自定义授权组件包装您的组件,如果用户未登录,则不要渲染您的组件。 尝试阻止 render 函数调用是不好的做法。

【讨论】:

【参考方案5】:

我认为这里的问题是authUser 是异步的。我会使用 Promise 以干净的方式处理异步响应。我不知道 firebase API,但显然支持承诺:https://firebase.google.com/docs/functions/terminate-functions

否则你可以使用像 bluebird 这样的库并修改你的 authUser 函数以返回一个承诺:http://bluebirdjs.com/docs/working-with-callbacks.html

如果你不熟悉 Promise,你应该首先阅读基础知识:https://bitsofco.de/javascript-promises-101/

【讨论】:

以上是关于ReactJs:如何在渲染之前等待 componentDidMount() 完成?的主要内容,如果未能解决你的问题,请参考以下文章

如何渲染存储在 redux reducer 中的 reactjs 组件?

如何等待在 react.js 中渲染视图,直到 $.get() 完成?

Reactjs - 路线中的“组件”与“渲染”

Reactjs动画替换:如何在添加内容淡入之前等待淡出?

在等待在 ReactJS 渲染中获取数据时显示加载微调器

从 ReactJS 外部渲染组件