我正在用rest框架和reactjs创建一个博客应用程序。在主页上的componentDidMount下,我使用axios发送API调用以获取所有文章和文章的setState返回。正如我所研究的那样,如果未为特定组件获取API,则axios会基于promise的想法,使代码不会继续进行。如果我错了,请告诉我。
然后,我发送GET调用以获取作者的姓名,该作者由ID撰写文章。虽然,我以为axios是可以实现的。但是,它不能那样工作。现在,我不确定该如何前进。
这是一个代码段。因此,在mainBody.js
中,我将api调用为:
class MainBody extends Component {
state = {};
componentDidMount () {
this.get_all_articles();
};
get_writer_name (id) {
let authstr = 'Bearer ' + window.localStorage.token;
let writer_url = "http://localhost:8000/api/writer/" + id.toString() + "/";
axios.get(writer_url, { headers: { Authorization: authstr }})
.then(response => {
console.log(response.data['name'])
return response.data['name'];
})
.catch(err => {
console.log("Got error")
})
};
get_all_articles () {
let authstr = 'Bearer ' + window.localStorage.token;
axios.get("http://localhost:8000/api/articles/", { headers: { Authorization: authstr }})
.then(response => {
this.setState({articles: response.data});
})
.catch(err => {
console.log("Got error")
})
}
render () {
return (
{this.state.articles.map((article, key) =>
<ArticleView key={article.id} article={article} writer_name={this.get_writer_name(article.created_by)} />
)}
)
}
}
在articleview2中,我将打印每篇文章中存在的所有数据以及作者的姓名。
我的articleview类是:
class ArticleView extends Component {
state = {article: this.props.article};
componentDidMount() {
console.log(this.props.writer_name;
}
render () {
return (
<React.Fragment>
<h2>{article.title}</h2>
<p>{article.body}</p>
<span>{this.props.writer_name}</span>
</React.Fragment>
)
}
}
如果仔细观察,我写了两个console.log语句来获取作者名称。根据该顺序,首先运行articleview类中存在的控制台日志(未定义),然后从API调用中获取数据,然后运行控制台日志,该控制台日志将返回正确的编写器名称。
我想知道,错误在哪里?另外,正如我注意到的那样,对于所有列出的文章,进行了太多的API调用以多次获得作者的名字。这些案例的行业最佳做法是什么?
答案 0 :(得分:1)
由于您的API调用有很多共同点,因此您应该首先设置一个axios实例,以重新使用这些通用功能:
const api = axios.create({
baseURL: 'http://localhost:8000/api/',
headers: { Authorization: `Bearer ${localStorage.token}` }
});
现在,由于您的MainBody
需要异步从API提取资源,因此将有很短的时间无法使用数据。有两种方法可以处理此问题。 MainBody
可能负责进行所有调用,或者仅负责进行调用以获取所有文章,然后每个ArticleView
组件都可以负责获取作者的姓名。我将在下面演示第一种方法:
class MainBody extends Component {
state = { articles: null, error: null, isLoading: true };
async componentDidMount () {
try {
const response = await api.get('articles/');
const articles = await Promise.all(
response.data.map(async article => {
const response = await api.get(`writer/${article.created_by}/`);
return { ...article, writer_name: response.data.name };
})
);
this.setState({ articles, isLoading: false });
} catch (error) {
this.setState({ error, isLoading: false });
}
}
render () {
const { articles, error, isLoading } = this.state;
return isLoading ? 'Loading...' : error
? `Error ${error.message}`
: articles.map(article => (
<ArticleView
key={article.id}
article={article}
writer_name={article.writer_name}
/>
)
);
}
}
答案 1 :(得分:0)
我想知道错误在哪里。
在编写this.state.articles.map()
时,意味着您正在使用Array
文章的属性映射,这些属性映射在获取数据之前可能是不确定的,这将导致错误Cannot read property map of undefined
。
解决方案
现在,由于API请求是异步的,这意味着render方法将不等待数据来临。因此,您可以做的是在状态中使用loader变量,并在发出请求时将其设置为true,然后在响应到来时将其设置为false,并在this.state时在render中显示加载器。 loader为true,如果为false,则显示文章。
或者,您也可以使用不会引起错误的空数组初始化this.state.articles。
此外,正如我注意到的那样,对于所有列出的文章,进行了太多的API调用,无法多次获得作者的名字。这些案例的行业最佳做法是什么?
在循环中发出API请求是非常糟糕的做法。一旦在公司做完,甚至我自己都会被责骂。
解决方案
您已经告诉后端工程师为您提供过滤器,以便在文章的每个对象中包括作者的姓名。我们在后端使用Loopback,它提供了一个过滤器,用于在内部将相关模型包括在每个对象中。