React JS:通过另一个对象数组过滤一个对象数组。如何顺序执行四个功能,包括几个API调用

时间:2018-10-10 22:09:10

标签: javascript arrays reactjs filtering sequential

目标是用另一个对象数组过滤一个对象数组。 每个阵列都来自不同的来源。

以下设置可能看起来很怪异,尽管由于某些未提及的原因而不幸地是必需的。

  • 第一步是从Firebase数据库中获取带有一些元数据的对象数组,这些元数据包含一些(最新)上传的帖子(所有帖子)。
  • 在第二步中,将为用户ID和文本ID(filterArrayIds)过滤此数组。
  • 在第三步中,将为每个过滤的用户ID调用一个API,以获取有关相应用户ID的所有现有帖子的详细信息。这些将合并为一个帖子数组(fetchJSONFiles)。
  • 在第四步中,应该为第二步(filterJSON)的textid数组中的所有textid过滤此合并的帖子数组。

到目前为止,以下是我的解决方案。不幸的是,我无法顺次执行这些函数,因此特别是在调用filterJSON()之前,fetchJSONfiles()已完全完成。

我在这里呆了几个小时……任何帮助都将不胜感激,这将使我过得愉快。谢谢!

示例数据:

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;

2 个答案:

答案 0 :(得分:1)

我建议这样修改:

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进行异步调用,因此其余同步代码将继续执行。

但是,通过从fetchJSONFiles返回Promise,我们可以“等待”。这使用了Promise.all功能,该功能基本上说:“ 创建一个诺言,当这个诺言数组中的每个诺言完成时,就完成”。

我们使用forEach而不是map,因为这使我们可以基于基本数组创建一个新数组,而不仅仅是循环遍历它。然后,我们不只是调用getFile,而是返回Promise链。因此,我们从this.state.userids创建了一个Promises数组,并从中创建了一个Promise,当使用Promise.all完成所有提取操作后,Promise便会解决。

然后,将其返回到位于componentDidMount中的初始Promise链中。这告诉链等待继续执行该Promise的结果(该Promise是this.fetchJSONFiles的结果),然后继续执行,在这种情况下,这将涉及执行finally回调。


现在,要考虑其他一些因素。即,如果fetchJSONFiles调用之一发生错误怎么办?这是您必须考虑的事情,但是这些更改应该可以使您起床并奔向想要的位置。

答案 1 :(得分:0)

.catch(function(error) {
    console.log("Error getting documents: ", error);
  })
  .then(() => {
          this.filterArrayIds();
  })
  .then(() => {
          this.fetchJSONFiles();
  })
  .finally(() => {
          this.filterJSON();
  });

当第一个承诺解决时,第一个.then()会被调用,第二个,第三个和.finally()也会被调用(无论您的承诺者是否解决,最终都会被调用)。他们都在期待解决的第一个诺言。

您需要执行的操作如下:

firebase
  .get()
  .then(snapshot => {
    // stuff happening with snapshot
    this.filterArrayIds().then(() => {
      this.fetchJSONFiles().then(() => {
        this.filterJSON();
      });
    });
  });

您的方法filterArraysId(),fetchJSONFiles()将必须返回一个Promise,该Promise会在完成工作后解决;)