在React Native中使用componentDidMount()
作为异步函数的良好做法还是应该避免使用它?
我需要在组件安装时从AsyncStorage
获取一些信息,但我知道的唯一方法是使componentDidMount()
函数异步。
async componentDidMount() {
let auth = await this.getAuth();
if (auth)
this.checkAuth(auth);
}
是否有任何问题,是否有其他解决方案可以解决这个问题?
答案 0 :(得分:77)
让我们首先指出差异并确定它如何引起麻烦。
以下是异步和“同步”componentDidMount()
生命周期方法的代码:
// This is typescript code
componentDidMount(): void { /* do something */ }
async componentDidMount(): Promise<void> {
/* do something */
/* You can use "await" here */
}
通过查看代码,我可以指出以下差异:
async
关键字:在打字稿中,这只是一个代码标记。它做了两件事:
Promise<void>
而不是void
。如果您明确指定返回类型为非承诺(例如:void),则typescript会向您发出错误。await
个关键字。void
更改为Promise<void>
async someMethod(): Promise<void> { await componentDidMount(); }
您现在可以在方法中使用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();
}
现在,它们怎么会引起麻烦?
async
关键字绝对无害。我无法想象您需要调用componentDidMount()
方法的任何情况,因此返回类型Promise<void>
也是无害的。
调用返回类型为Promise<void>
但没有await
关键字的方法与调用返回类型为void
的方法没有区别。
由于在componentDidMount()
延迟执行后没有生命周期方法似乎非常安全。但是有一个问题。
假设上述this.setState({users, questions});
将在10秒后执行。在延迟时间的中间,另一个......
this.setState({users: newerUsers, questions: newerQuestions});
...已成功执行并且DOM已更新。结果对用户可见。时钟继续滴答,10秒钟过去了。然后执行延迟的this.setState(...)
并再次更新DOM,那个时间用旧用户和旧问题。用户也可以看到结果。
=&GT;使用async
方法componentDidMount()
非常安全(我不确定100%)。我是它的忠实粉丝,到目前为止我还没有遇到任何令我头疼的问题。
答案 1 :(得分:6)
您的代码很好,对我来说非常易读。请参阅此Dale Jefferson's article,其中显示了一个异步componentDidMount
示例,看起来也非常好。
但有些人会说,阅读代码的人可能会认为React对返回的承诺做了一些事情。
因此,对此代码的解释以及是否是一种好的做法是非常个人化的。
如果您想要其他解决方案,可以使用promises。例如:
componentDidMount() {
fetch(this.getAuth())
.then(auth => {
if (auth) this.checkAuth(auth)
})
}
答案 2 :(得分:2)
实际上,随着React从传统生命周期方法(componentWillMount,componentWillReceiveProps,componentWillUpdate)转移到异步渲染,ComponentDidMount中的异步加载是一种推荐的设计模式。
这篇博客文章对于解释为什么这样做安全并为ComponentDidMount中的异步加载示例提供了非常有用的帮助:
https://reactjs.org/blog/2018/03/27/update-on-async-rendering.html
答案 3 :(得分:1)
更新:
(我的构建:React 16,Webpack 4,Babel 7):
使用Babel 7时,您会发现:
使用此模式...
async componentDidMount() {
try {
const res = await fetch(config.discover.url);
const data = await res.json();
console.log(data);
} catch(e) {
console.error(e);
}
}
您将遇到以下错误...
未捕获的ReferenceError:未定义regeneratorRuntime
在这种情况下,您将需要安装 babel-plugin-transform-runtime
https://babeljs.io/docs/en/babel-plugin-transform-runtime.html
如果由于某些原因您不希望安装上述软件包(babel-plugin-transform-runtime),那么您将希望遵循Promise模式...
componentDidMount() {
fetch(config.discover.url)
.then(res => res.json())
.then(data => {
console.log(data);
})
.catch(err => console.error(err));
}
答案 4 :(得分:1)
我认为只要您知道自己在做什么就可以。但这可能会造成混乱,因为async componentDidMount()
在运行componentWillUnmount
并且卸载了组件后仍可以运行。
您可能还想在componentDidMount
中启动同步和异步任务。如果componentDidMount
是异步的,则必须将所有同步代码放在第一个await
之前。对于某人来说,第一个await
之前的代码是同步运行的,这可能并不明显。在这种情况下,我可能会保持componentDidMount
同步,但是让它调用sync和async方法。
无论您选择async componentDidMount()
还是同步componentDidMount()
调用async
方法,都必须确保清除组件卸载后仍在运行的所有侦听器或异步方法。 / p>
答案 5 :(得分:1)
我喜欢用这样的东西
componentDidMount(){
const result = makeResquest()
}
async makeRequest(){
const res = await fetch(url);
const data = await res.json();
return data
}
答案 6 :(得分:0)
当您使用不带componentDidMount
关键字的async
时,医生会这样说:
您可以立即在componentDidMount()中调用setState()。它会触发额外的渲染,但是会在浏览器更新屏幕之前发生。
如果您使用async componentDidMount
,则会失去此功能:浏览器更新屏幕后,将再次渲染。但是imo,如果您正在考虑使用异步操作(例如获取数据),则无法避免浏览器将屏幕更新两次。在另一个世界中,无法在浏览器更新屏幕之前暂停componentDidMount
答案 7 :(得分:0)
我做了一些研究,发现了一个重要的区别: React不会处理异步生命周期方法中的错误。
因此,如果您编写这样的内容:
componentDidMount()
{
throw new Error('I crashed!');
}
然后error boundry将捕获您的错误,您可以对其进行处理并显示优美的消息。
如果我们这样更改代码:
async componentDidMount()
{
throw new Error('I crashed!');
}
等效于此:
componentDidMount()
{
return Promise.reject(new Error('I crashed!'));
}
然后您的错误将被静默吞下。可耻的是,React ...
那么,我们如何处理错误呢?唯一的方法似乎是像这样的显式捕获:
componentDidMount()
{
try
{
await myAsyncFunction();
}
catch(error)
{
//...
}
}
或类似这样:
componentDidMount()
{
myAsyncFunction()
.catch(()=>
{
//...
});
}
如果我们仍然希望我们的错误丰富错误边界,那么我可以考虑以下技巧:
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>;
}
}