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 组件?