React JS:通过另一个对象数组过滤一个对象数组。如何顺序执行四个功能,包括。几个 API 电话
React JS: Filtering an array of objects by another array of objects. How to sequential execute four functions incl. several API calls
目标是用另一个对象数组过滤一个对象数组。
每个数组都来自不同的来源。
以下设置可能看起来很奇怪,但由于几个此处未提及的原因不幸是必要的。
- 第一步是从 Firebase 数据库中获取一个对象数组,其中包含一些(最新)上传的帖子 (allposts) 的元数据。
- 在第二步中,将针对用户 ID 和文本 ID (filterArrayIds) 过滤此数组。
- 在第三步中,对于每个过滤后的用户 ID,将调用 API 以获取相应用户 ID 的所有现有帖子的详细信息。这些将合并到一组帖子中 (fetchJSONFiles)。
- 在第四步中,应该针对第二步 (filterJSON) 的 textid 数组中的所有 textid 过滤这个合并的帖子数组。
以下是我目前的解决方案。不幸的是,我无法按顺序执行函数,因此尤其是 fetchJSONfiles() 在调用 filterJSON() 之前完全完成。
我被困在这里好几个小时了……非常感谢任何帮助,这会让我很开心。谢谢!
示例数据:
allposts: [
{
dateofpost: "1539181118111",
textid: "1",
userid: "Alice",
},
{
dateofpost: "1539181118222",
textid: "3",
userid: "Bob",
},
]
-
allfilteredTexts: [
{
title: "Lorem",
textid: "1",
},
{
title: "Ipsum",
textid: "2",
},
{
title: "Dolor",
textid: "3",
},
]
预期结果:
latestPosts: [
{
title: "Lorem",
textid: "1",
},
{
title: "Dolor",
textid: "3",
},
]
目前我的解决方案:
class Explore extends React.Component {
constructor(props) {
super(props);
this.state = {
allposts: [],
textids: [],
userids: [],
allfilteredTexts: [],
};
}
componentDidMount() {
const allfilteredTexts = {...this.state.allfilteredTexts}
firebase
.firestore()
.collection("allposts")
.orderBy("dateofpost")
.get()
.then(snapshot => {
const allposts = this.state.allposts;
snapshot.forEach(doc => {
allposts.push({
userid: doc.data().userid,
textid: doc.data().textid,
dateofpost: doc.data().dateofpost,
});
});
this.setState({
allposts: allposts,
});
})
.catch(function(error) {
console.log("Error getting documents: ", error);
})
.then(() => {
this.filterArrayIds();
})
.then(() => {
this.fetchJSONFiles();
})
.finally(() => {
this.filterJSON();
});
}
filterArrayIds() {
var userids = this.state.userids
var textids = this.state.textids
if (this.state.allposts) {
var filtereduserids = [...new Set([].concat(...this.state.allposts.map(o => o.userid)))];
var filteredtextids = [...new Set([].concat(...this.state.allposts.map(p => p.textid)))];
this.setState({
userids: filtereduserids,
textids: filteredtextids,
})
}
}
fetchJSONFiles() {
if (this.state.userids) {
this.state.userids.forEach((username) => {
var filteredTexts = []
const options = {username} //here would be more API options //
getFile(options)
.then((file) => {
filteredTexts = JSON.parse(file || '[]');
})
.then (() => {
Array.prototype.push.apply(filteredTexts, this.state.allfilteredTexts);
this.setState({
allfilteredTexts: filteredTexts,
})
})
}
}
filterJSON(){
let latestPosts = (this.state.allfilteredTexts.filter(
(el) => { return el.id.indexOf(this.state.textids) !== -1;
}));
}
render () {
return (
<div>
<Switch>
<Route
path='/explore/latest/'
render={(props) => <ExploreLatest {...props} allposts={this.state.allposts} allfilteredTexts={this.state.allfilteredTexts} />}
/>
</Switch>
</div>
)
}
}
export default Explore;
我建议这样修改:
fetchJSONFiles() {
if (this.state.userids) {
return Promise.all(this.state.userids.map((username) => {
var filteredTexts = []
const options = {username} //here would be more API options //
return getFile(options)
.then((file) => {
filteredTexts = JSON.parse(file || '[]');
})
.then (() => {
Array.prototype.push.apply(filteredTexts, this.state.allfilteredTexts);
this.setState({
allfilteredTexts: filteredTexts,
})
}))
}
}
然后是行:
.then(() => {
this.fetchJSONFiles();
})
可以变成:
.then(() => {
return this.fetchJSONFiles();
})
为什么?
fetchJSONFiles
没有在 Promise 链的其余部分之前完成的原因是因为 Promise 链不知道等待 fetchJSONFiles
的结果。 fetchJSONFiles
进行异步调用,因此其余的同步代码继续执行。
但是,通过 return 从 fetchJSONFiles 中获取 Promise,我们可以 "wait on"。这使用了特性Promise.all
,它基本上是说“创建一个承诺,当这个承诺数组中的每个承诺完成时完成”。
我们使用 map
而不是 forEach
,因为这允许我们基于基本数组创建一个新数组,而不是仅仅循环它。然后我们不只是调用 getFile
,而是 return Promise 链。因此,我们从 this.state.userids
创建一个 Promise 数组,并从 Promise.all
.
完成所有提取后将解析的 Promise 创建一个
然后我们 return 在位于 componentDidMount
的初始 Promise 链中。这告诉链在继续之前等待该 Promise 的结果(该 Promise 是 this.fetchJSONFiles
的结果)完成,在这种情况下将涉及执行 finally
回调。
现在,还有其他一些注意事项需要……考虑。也就是说,如果 fetchJSONFiles
调用之一出现错误会怎样?这是你必须要考虑的事情,但这些变化应该会让你振作起来,运行你想去的地方。
.catch(function(error) {
console.log("Error getting documents: ", error);
})
.then(() => {
this.filterArrayIds();
})
.then(() => {
this.fetchJSONFiles();
})
.finally(() => {
this.filterJSON();
});
第一个 .then() 在第一个 promise 解析时被调用,第二个、第三个和 .finally() 也是如此(无论您的 promise 是否解析,finally 都会被调用)。他们都在等待第一个解决的承诺。
您需要做的是:
firebase
.get()
.then(snapshot => {
// stuff happening with snapshot
this.filterArrayIds().then(() => {
this.fetchJSONFiles().then(() => {
this.filterJSON();
});
});
});
您的方法 filterArraysId()、fetchJSONFiles() 必须 return 一个 Promise,当它们完成工作时会解析 ;)
目标是用另一个对象数组过滤一个对象数组。 每个数组都来自不同的来源。
以下设置可能看起来很奇怪,但由于几个此处未提及的原因不幸是必要的。
- 第一步是从 Firebase 数据库中获取一个对象数组,其中包含一些(最新)上传的帖子 (allposts) 的元数据。
- 在第二步中,将针对用户 ID 和文本 ID (filterArrayIds) 过滤此数组。
- 在第三步中,对于每个过滤后的用户 ID,将调用 API 以获取相应用户 ID 的所有现有帖子的详细信息。这些将合并到一组帖子中 (fetchJSONFiles)。
- 在第四步中,应该针对第二步 (filterJSON) 的 textid 数组中的所有 textid 过滤这个合并的帖子数组。
以下是我目前的解决方案。不幸的是,我无法按顺序执行函数,因此尤其是 fetchJSONfiles() 在调用 filterJSON() 之前完全完成。
我被困在这里好几个小时了……非常感谢任何帮助,这会让我很开心。谢谢!
示例数据:
allposts: [
{
dateofpost: "1539181118111",
textid: "1",
userid: "Alice",
},
{
dateofpost: "1539181118222",
textid: "3",
userid: "Bob",
},
]
-
allfilteredTexts: [
{
title: "Lorem",
textid: "1",
},
{
title: "Ipsum",
textid: "2",
},
{
title: "Dolor",
textid: "3",
},
]
预期结果:
latestPosts: [
{
title: "Lorem",
textid: "1",
},
{
title: "Dolor",
textid: "3",
},
]
目前我的解决方案:
class Explore extends React.Component {
constructor(props) {
super(props);
this.state = {
allposts: [],
textids: [],
userids: [],
allfilteredTexts: [],
};
}
componentDidMount() {
const allfilteredTexts = {...this.state.allfilteredTexts}
firebase
.firestore()
.collection("allposts")
.orderBy("dateofpost")
.get()
.then(snapshot => {
const allposts = this.state.allposts;
snapshot.forEach(doc => {
allposts.push({
userid: doc.data().userid,
textid: doc.data().textid,
dateofpost: doc.data().dateofpost,
});
});
this.setState({
allposts: allposts,
});
})
.catch(function(error) {
console.log("Error getting documents: ", error);
})
.then(() => {
this.filterArrayIds();
})
.then(() => {
this.fetchJSONFiles();
})
.finally(() => {
this.filterJSON();
});
}
filterArrayIds() {
var userids = this.state.userids
var textids = this.state.textids
if (this.state.allposts) {
var filtereduserids = [...new Set([].concat(...this.state.allposts.map(o => o.userid)))];
var filteredtextids = [...new Set([].concat(...this.state.allposts.map(p => p.textid)))];
this.setState({
userids: filtereduserids,
textids: filteredtextids,
})
}
}
fetchJSONFiles() {
if (this.state.userids) {
this.state.userids.forEach((username) => {
var filteredTexts = []
const options = {username} //here would be more API options //
getFile(options)
.then((file) => {
filteredTexts = JSON.parse(file || '[]');
})
.then (() => {
Array.prototype.push.apply(filteredTexts, this.state.allfilteredTexts);
this.setState({
allfilteredTexts: filteredTexts,
})
})
}
}
filterJSON(){
let latestPosts = (this.state.allfilteredTexts.filter(
(el) => { return el.id.indexOf(this.state.textids) !== -1;
}));
}
render () {
return (
<div>
<Switch>
<Route
path='/explore/latest/'
render={(props) => <ExploreLatest {...props} allposts={this.state.allposts} allfilteredTexts={this.state.allfilteredTexts} />}
/>
</Switch>
</div>
)
}
}
export default Explore;
我建议这样修改:
fetchJSONFiles() {
if (this.state.userids) {
return Promise.all(this.state.userids.map((username) => {
var filteredTexts = []
const options = {username} //here would be more API options //
return getFile(options)
.then((file) => {
filteredTexts = JSON.parse(file || '[]');
})
.then (() => {
Array.prototype.push.apply(filteredTexts, this.state.allfilteredTexts);
this.setState({
allfilteredTexts: filteredTexts,
})
}))
}
}
然后是行:
.then(() => {
this.fetchJSONFiles();
})
可以变成:
.then(() => {
return this.fetchJSONFiles();
})
为什么?
fetchJSONFiles
没有在 Promise 链的其余部分之前完成的原因是因为 Promise 链不知道等待 fetchJSONFiles
的结果。 fetchJSONFiles
进行异步调用,因此其余的同步代码继续执行。
但是,通过 return 从 fetchJSONFiles 中获取 Promise,我们可以 "wait on"。这使用了特性Promise.all
,它基本上是说“创建一个承诺,当这个承诺数组中的每个承诺完成时完成”。
我们使用 map
而不是 forEach
,因为这允许我们基于基本数组创建一个新数组,而不是仅仅循环它。然后我们不只是调用 getFile
,而是 return Promise 链。因此,我们从 this.state.userids
创建一个 Promise 数组,并从 Promise.all
.
然后我们 return 在位于 componentDidMount
的初始 Promise 链中。这告诉链在继续之前等待该 Promise 的结果(该 Promise 是 this.fetchJSONFiles
的结果)完成,在这种情况下将涉及执行 finally
回调。
现在,还有其他一些注意事项需要……考虑。也就是说,如果 fetchJSONFiles
调用之一出现错误会怎样?这是你必须要考虑的事情,但这些变化应该会让你振作起来,运行你想去的地方。
.catch(function(error) {
console.log("Error getting documents: ", error);
})
.then(() => {
this.filterArrayIds();
})
.then(() => {
this.fetchJSONFiles();
})
.finally(() => {
this.filterJSON();
});
第一个 .then() 在第一个 promise 解析时被调用,第二个、第三个和 .finally() 也是如此(无论您的 promise 是否解析,finally 都会被调用)。他们都在等待第一个解决的承诺。
您需要做的是:
firebase
.get()
.then(snapshot => {
// stuff happening with snapshot
this.filterArrayIds().then(() => {
this.fetchJSONFiles().then(() => {
this.filterJSON();
});
});
});
您的方法 filterArraysId()、fetchJSONFiles() 必须 return 一个 Promise,当它们完成工作时会解析 ;)