Как избежать дублирования данных и вызовов API в плоской структуре vuex? - PullRequest
0 голосов
/ 05 августа 2020

В настоящее время я создаю проект vue / vuex / firestore / vuexFire и столкнулся со следующей проблемой:

Скажем, я хочу отобразить список сообщений, в которых каждое сообщение связано с пользователем. Пользователь может отправить несколько сообщений, и все они будут на свое имя. В vuex плоское представление этого может выглядеть так:

Модуль состояния пользователя

state: () => ({
  users: [
   {
    name: 'foo',
    id: 'bar'
   }
   //...
  ]
})

Модуль состояния сообщений

state: () => ({
  messages: [
   {
    message: 'blah blah',
    authorId: 'bar'
   }
   //...
  ]
})

На сервере (я использую Firebase's Firestore) структура по сути такая же. Итак, я получаю все сообщения, а затем следую за каждым автором, вот так:

// action in the vuex messages module
{
 fetchMessages: async ({ commit, dispatch }) => {
  
  const snapshot = await firestore.collection('messages').get();
  snapshot.forEach((doc) => {
   
   // commit the document
   commit('addMessage', doc.data())
   
   // fetch the associated user
   dispatch('fetchUser', doc.data().authorId)
  })
 },

 fetchUser: async ({commit}, id) => {
  const user = await firestore.collection('users').doc(id).get();
  commit('addUser', user.data())
 }

}

Проблема, с которой я столкнулся, заключается в том, что если есть пользователь, который создал несколько сообщений, они будут добавлены к пользователям. массив снова и снова. Кроме того, fetchUser() вызывается без надобности снова и снова. До сих пор мои попытки решить эту проблему не увенчались успехом или были неэлегантными:

  • Я попытался проверить, присутствует ли пользователь в массиве users, прежде чем вызывать fetchUser(). Однако это не работает, потому что fetchUser является асинхронной операцией c. Это могло быть в процессе выборки, поэтому проверка того, есть ли у пользователя уже в массиве, обычно проверяет false.

  • Я пытался проверить, есть ли у пользователя уже присутствует в мутации addUser. Хотя это предотвращает дублирование, это не предотвращает ненужную выборку пользователем. Кроме того, мутация addUser должна проверять, что пользователь не является дубликатом при каждом вызове, что может быть дорогостоящим с большими массивами.

  • Один из подходов, который работал, - это создание fetchJobs объект в состоянии users. Когда был вызван fetchUser(), я добавил к объекту идентификатор пользователя. Затем, прежде чем получать дополнительных пользователей, я бы проверил этот объект, чтобы убедиться, что я не получаю дубликат. Причина, по которой это сработало, в отличие от прямой проверки состояния, заключается в том, что я зафиксировал fetchJob в начале действия, поэтому он не был асинхронным. Таким образом, другие компоненты и модули состояния могли получить к нему доступ. Однако это не работает, если пользовательский объект необходимо обновить в любой момент, поскольку он все равно останется в объекте fetchJobs.

Я также должен упомянуть, что я использую небольшой вспомогательная библиотека VuexFire, которая управляет привязками для Firestore collectionReference.onSnapshot(). Это ввело еще одно ограничение: я не могу напрямую запускать действие, когда документ добавляется, обновляется или удаляется. Это связано с тем, что VuexFire обрабатывает все эти изменения автоматически и не поддерживает привязку к этим событиям. Вместо этого я должен oop просмотреть все существующие сообщения и позвонить fetchUser(). Я не включил это в приведенные выше примеры кода, потому что он не сильно отличается от snapshot.forEach(). При необходимости я могу отказаться от этой библиотеки.

Итак, как лучше всего решить эту проблему?

1 Ответ

2 голосов
/ 05 августа 2020

Из всего, что вы набрали, кажется, что предотвращение ненужного вызова действия fetchUser имеет первостепенное значение, поэтому я сосредоточусь на одной попытке, которую вы пробовали, чтобы этого не произошло

Я попытался проверить, присутствует ли уже пользователь в массиве пользователей перед вызовом fetchUser (). Однако это не работает, потому что fetchUser - это асинхронная c операция. Это могло быть в процессе выборки, поэтому проверка того, находится ли пользователь уже в массиве, обычно проверяет false

Как насчет этого?

{
 fetchMessages: async ({ commit, dispatch }) => {
  
  const snapshot = await firestore.collection('messages').get();
  snapshot.forEach((doc) => {
   
   // commit the document
   commit('addMessage', doc.data())
   
   const userIndexInTheUsersStoreArray = store.state.users.findIndex(user => user.id === doc.data().authorId)
   
   const isUserInTheUsersStoreArray = userIndexInTheUsersStoreArray > -1
   
   if (!userInTheUsersStoreArray) {
    dispatch('fetchUser', doc.data().authorId)
   }
  })
 },
...