【问题标题】:ReactJs: How to wait for componentDidMount() to finish before rendering?ReactJs:如何在渲染之前等待 componentDidMount() 完成?
【发布时间】: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()?

【问题讨论】:

    标签: reactjs firebase


    【解决方案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/

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2021-11-21
            • 2016-04-08
            • 2020-06-17
            • 1970-01-01
            • 2022-12-14
            • 2014-11-15
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多