我有一个从外部API加载应用程序列表的类组件。然后,它为每个应用程序运行一个额外的请求来ping它。
它可以很好地获取应用程序。然而,它只是有时ping应用程序(调用checkPing)。
组件:
import axios from "axios";
import React from "react";
class Applications extends React.Component<any, any> {
constructor() {
super({});
this.state = {
applications: []
}
}
async checkPing(applicationId : bigint) {
//
}
async getApplications() {
var component = this;
await axios.get(process.env.REACT_APP_API_URL + '/applications')
.then(async function (response) {
await component.setState({
applications: response.data,
});
})
.catch(function (error) {
// handle error
console.log(error);
})
.finally(function () {
// always executed
});
for (let i = 0; i < component.state.applications.length; i++) {
await component.checkPing(component.state.applications[i].id);
}
}
async componentDidMount() {
await this.getApplications();
}
render() {
return (<div></div>)
}
}
export default Applications;
正如评论中提到的,
你要么需要将for循环移到block中,要么可以使用componentdiduupdate生命周期钩子。Axios调用是异步的,并且你的for循环当前没有等待它完成。
下面是使用componentDidUpdate 的示例import React from "react";
import axios from "axios";
import "./styles.css";
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
applications: []
};
}
async checkPing(applicationId) {
console.log("pinging " + applicationId);
}
async getApplications() {
var component = this;
try {
const response = await axios.get("./data.json");
component.setState({ applications: response.data });
} catch (error) {
// handle error
console.log(error);
}
}
componentDidMount() {
this.getApplications();
}
componentDidUpdate() {
for (let i = 0; i < this.state.applications.length; i++) {
this.checkPing(this.state.applications[i].id);
}
}
render() {
return <div>HERE</div>;
}
}
export default App;
如果你不想使用componentDidUpdate,那么你可以把for循环移进去,然后像这样调用。
import React from "react";
import axios from "axios";
import "./styles.css";
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
applications: []
};
}
async checkPing(applicationId) {
console.log("pinging " + applicationId);
}
async getApplications() {
var component = this;
try {
const response = await axios.get("./data.json");
component.setState({ applications: response.data });
for (let i = 0; i < response.data.length; i++) {
this.checkPing(response.data[i].id);
}
} catch (error) {
// handle error
console.log(error);
}
}
componentDidMount() {
this.getApplications();
}
render() {
return <div>HERE</div>;
}
}
export default App;
这里的主要问题可能是setState()
将状态更改排队,而不是直接应用它。setState()
不返回承诺。这意味着await component.setState(...)
是无用的。知道React何时完成状态更新的唯一方法是使用componentDidUpdate()
钩子。
这个钩子的实现,使用你的例子可能是这样的:
import axios from "axios";
import React from "react";
class Applications extends React.Component<any, any> {
constructor(props) {
super(props);
this.state = {
isLoading: true,
error: null,
applications: [],
}
}
async checkPing(applicationId : bigint) {
// ...
}
async getApplications() {
// There is no need to work with `then()`, `catch()` or `finally()`
// when we are within async context. We can use the respective
// keywords instead.
try {
const response = await axios.get(process.env.REACT_APP_API_URL + '/applications');
this.setState({ applications: response.data });
} catch (error) {
// handle error
this.setState({ error });
} finally {
// always executed
this.setState({ isLoading: false });
}
}
// Content can also be placed in `componentDidMount()` instead of
// its own method.
async pingApplications() {
for (const application of this.state.applications) {
await this.checkPing(application.id);
}
}
componentDidMount() {
this.getApplications();
}
componentDidUpdate(prevProps, prevState) {
if (this.state.isLoading) return;
if (this.state.error) return;
this.pingApplications();
}
render() {
const { isLoading, error, applications } = this.state;
if (isLoading) return (
<div className="loading">loading...</div>
);
if (error) return (
<div className="error">{error.message}</div>
);
return (
<div>content</div>
);
}
}
export default Applications;