Реагируйте JS: Фильтрация массива объектов другим массивом объектов.Как последовательно выполнить четыре функции вкл.несколько вызовов API - PullRequest
0 голосов
/ 11 октября 2018

Цель состоит в том, чтобы фильтровать массив объектов другим массивом объектов.Каждый массив поступает из разных источников.

Следующая настройка может показаться странной, хотя по нескольким здесь не упомянутым причинам, к сожалению, необходимо.

  • Шаг первый - получить из базы данных Firebaseмассив объектов с метаданными о некоторых (самых последних) загруженных сообщениях (allposts).
  • На втором шаге этот массив будет отфильтрован для идентификаторов пользователей и текстов (filterArrayIds).
  • На третьем шаге для каждого отфильтрованного идентификатора пользователя будет вызван API для получения подробной информации обо всех существующих публикациях соответствующего идентификатора пользователя.Они будут объединены в один массив сообщений (fetchJSONFiles).
  • На четвертом шаге этот объединенный массив сообщений должен быть отфильтрован по всем текстам в массиве текстов шага два (filterJSON).

Ниже мое решение до сих пор.К сожалению, я не могу сделать так, чтобы функции выполнялись последовательно, так что особенно 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;

Ответы [ 2 ]

0 голосов
/ 11 октября 2018
.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 () должны будут вернутьсяОбещание, которое разрешается, когда они закончили работать;)

0 голосов
/ 11 октября 2018

Я бы предложил изменить так:

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 выполняет асинхронные вызовы, поэтому остальная часть синхронного кода продолжает выполняться.

Однако, возвращая Promise из fetchJSONFiles, у нас есть что-то, чтобы «подождать».При этом используется функция Promise.all, которая в основном говорит: " создайте одно обещание, которое заканчивается, когда каждое обещание в этом массиве обещаний завершается ".

Вместо forEach мы используем map, потому что это позволяет нам создавать новый массив на основе базового массива, а не просто зацикливаться на нем.И затем вместо того, чтобы просто позвонить getFile, мы возвращаем цепочку обещаний.Таким образом, мы создаем массив Обещаний из this.state.userids и создаем одно Обещание из того, которое будет разрешено, когда все выборки будут выполнены с Promise.all.

Затем мы возвращаем это в исходной цепочке Обещаний, которая находитсяв componentDidMount.Это говорит цепочке ждать, пока результат этого Обещания (это Обещание является результатом this.fetchJSONFiles) будет завершен, прежде чем продолжить, что в этом случае потребует выполнения обратного вызова finally.


Теперь есть некоторые другие соображения, чтобы ... рассмотреть.А именно, что произойдет, если в одном из вызовов fetchJSONFiles возникнет ошибка?Это то, о чем вам нужно подумать, но эти изменения должны помочь вам достичь желаемого.

...