我正在React/Redux上构建一个hackernews克隆。
在组件 Item.js 内部,我调用 this.props.fetchItem(this.props.match.params.id);
以下是fetchItem
动作创建者:
export const fetchItem = (id) => {
return dispatch => {
return axios.get(`${ROOT_API_URL}/item/${id}.json`)
.then(response => {
let story = response.data;
let commentIds = response.data.kids; // Story kids
var allComments = [];
let fetchComments = commentIdArray => {
let promiseArray = [];
// 1. Create requests for each commentId
commentIdArray.forEach(commentId => {
promiseArray.push(axios.get(`${ROOT_API_URL}/item/${commentId}.json`));
})
// 2. Make requests to fetch all commentIds
axios.all(promiseArray)
.then(responseArray => {
responseArray.forEach(response => {
let comment = response.data;
allComments.push(comment);
if(comment.kids){
fetchComments(comment.kids);
}else{
return false;
}
})
console.log("allComments: ", allComments);
})
.catch(error => {
console.log("error promise.all: ", error);
})
}
fetchComments(commentIds)
})
.catch(error => {
if(error.response.status === 401){
console.error("Unauthorized");
}else{
console.error("Something went wrong!");
}
})
}
}
基本上,它接收故事的 ID 并获取该故事的评论。这部分也有效。我在一个扁平的数组中得到了所有评论。
这就是问题所在。我必须在fetchItem
某处调用fetchItemSuccess
,以便我可以将数据保存到 redux。虽然,我不知道有多少评论,我正在通过他们的子评论递归获取它们。
那么,我怎么知道我已经准备好了所有评论,然后打电话给fetchItemSuccess
?
一个故事:https://hacker-news.firebaseio.com/v0/item/8863.json?print=pretty
评论:https://hacker-news.firebaseio.com/v0/item/8952.json?print=pretty
通了。
我只需要跟踪异步方法调用。因此,我添加了一个计数变量。如果计数为零,则调度操作。
export const fetchItem = (id) => {
return dispatch => {
return axios.get(`${ROOT_API_URL}/item/${id}.json`)
.then(response => {
let story = response.data;
let commentIds = response.data.kids; // Story kids
var allComments = [];
var count = 0; // <=== New Line
let fetchComments = (commentIdArray) => {
let promiseArray = [];
count++; // <=== New Line
// 1. Create requests for each commentId
commentIdArray.forEach(commentId => {
promiseArray.push(axios.get(`${ROOT_API_URL}/item/${commentId}.json`));
})
// 2. Make requests to fetch all commentIds
axios.all(promiseArray)
.then(responseArray => {
responseArray.forEach(response => {
let comment = response.data;
allComments.push(comment);
if(comment.kids){
fetchComments(comment.kids);
}else{
return false;
}
})
count--; // <=== New Line
if(count === 0){ // <=== New Line
dispatch( fetchItemSuccess(allComments) );
}
})
.catch(error => {
console.log("error promise.all: ", error);
})
}
fetchComments(commentIds);
})
.catch(error => {
if(error.response.status === 401){
console.error("Unauthorized");
}else{
console.error("Something went wrong!");
}
})
}
}
下面是一个示例,说明您的源结构可能更合理一些:
const fetchComments = function(commentIdArray) {
const promiseArray = [];
// 1. Create requests for each commentId
commentIdArray.forEach(commentId => {
promiseArray.push(axios.get(`${ROOT_API_URL}/item/${commentId}.json`));
});
let parentCommentsComments = [];
return axois.all(promiseArray).then(responseArray => {
responseArray.forEach(response => {
const comment = response.data;
parentComments.push(comment);
if (comment.kids) {
return fetchComments(comment.kids);
} else {
return [];
}
});
}).then(kidsComments => {
return parentComments.concat(kidsComments);
});
});
export const fetchItem = (id) => {
return dispatch => {
return axios.get(`${ROOT_API_URL}/item/${id}.json`)
.then(response => {
const story = response.data;
const commentIds = response.data.kids; // Story kids
return fetchComments(commentIds);
})
.then(comments => {
// do something with comments?
})
.catch(error => {
if(error.response.status === 401){
console.error("Unauthorized");
}else{
console.error("Something went wrong!");
}
})
}
}