Firebase: почему событие value запускается перед добавлением нового дочернего ссылки - PullRequest
0 голосов
/ 25 мая 2019

Следующий код - очень простое приложение Firebase - VueJS, ( codeSandBox demo )

app.vue

<template>
  <div class="container">
    <!-- Adding Quote -->
    <add-quote/>

    <!-- Display Quotes -->
    <quote-list/>
  </div>
</template>

<script>
import addQuote from "./components/AddQuote.vue";
import quoteList from "./components/QuoteList.vue";

export default {
  components: {
    addQuote,
    quoteList
  },
  methods: {
    get_allQuotes: function() {
      // var vm = this;
      var localArr = [];
      quotesRef
        .once("value", function(snapshot) {
          snapshot.forEach(function(snap) {
            localArr.push({
              key: snap.key,
              category: snap.val().category,
              quoteTxt: snap.val().quoteTxt
            });
          });
        })
        .then(data => {
          this.$store.commit("set_allQuotes", localArr);
        });
    }
  },
  mounted() {
    this.get_allQuotes();
    console.log("App: mounted fired");
  }
};
</script>

store.js (vuex store)

import Vue from "vue";
import Vuex from "vuex";

Vue.use(Vuex);

export const store = new Vuex.Store({
    state: {
        quotesList: []
    },
    getters: {
        get_quotesList(state) {
            return state.quotesList;
        }
    },
    mutations: {
        set_allQuotes(state, value) {
            state.quotesList = value;
        }
    }
});

AddQuote.vue

<template>     
    <div class="row quote-edit-wrapper">         
        <div class="col-xs-6">           
            <textarea v-model.lazy="newQuoteTxt" 
                rows="4" 
                cols="50"></textarea>
            <button @click="addQuote">Add Quote</button>
        </div>
    </div>
</template>

<script>

export default {   
  data() {
    return {
            newQuoteTxt: '',
      }
    },
    computed: {
    allQuotes() {
      return this.$store.getters.get_quotesList;
        },
        newQuoteIdx() {
            var localArr = [...this.allQuotes]

            if(localArr.length > 0) {
                var highestKEY, currKEY

                localArr.forEach((element, idx) => {
                    currKEY = parseInt(element.key)
                    if(idx == 0) {
                        highestKEY = currKEY
                    } else {
                        if(highestKEY < currKEY) {
                            highestKEY = currKEY
                        }
                    }
                })
                return highestKEY + 1
            } else {
                return 1
            }
        }
    },
  methods: {
        // ADD new Quote in DB
    addQuote: function() {
            var vm = this
            var localArr = [...this.allQuotes]

            //1. First attach 'value' event listener,
            // Snapshot will contain data from that ref 
            // when any child node is added/updated/delete
            quotesRef.on('value', function (snapshot) {
                snapshot.forEach(function(snap) {
                var itemExists = localArr.some(function (item, idx) {
                        return item.key == snap.key
                    })
                // If newly added item doesn't yet exists then add to local array
                if (!(itemExists)) {
                    localArr.push({
                        key: snap.key,
                        category: snap.val().category,
                        quoteTxt: snap.val().quoteTxt })

                    vm.$store.commit('set_allQuotes', localArr)
                    }
                })
            })

            //2. Second set/create a new quotes in Firebase, 
            // When this quote gets added in Firebase,
            // value event (attached earlier) gets fired  
            //  with 
            var newQuoteRef = quotesRef.child(this.newQuoteIdx)
            newQuoteRef.set({
                category: 'motivation',
                quoteTxt: this.newQuoteTxt
            })
        }
    }
}
</script>

quoteList.vue

<template>
    <div class="row">
        <div class="col-xs-12 quotes-list-wrapper">
            <template v-for="(quote,idx) in allQuotes">

                <!-- Quote block -->
                    <div class="quote-block-item">
                        <p class="quote-txt"> {{quote.quoteTxt}} </p>
                    </div>
            </template>
        </div>
    </div>  
</template>

<script>
    export default {
        computed: {
            allQuotes() {
                return this.$store.getters.get_quotesList;
            }
        }
    }

</script>

Примечание: Основной код концерна: addQuote.vue

Пользователь вводит newQuoteTxt, который добавляется в Firebase (addQuote()) как элемент цитаты под quotesRef. Как только цитата добавляется (на firebase), запускается событие value SDK на стороне клиента Firebase, и добавляется новая цитата (с помощью обратного вызова) в localArray (allQuotes). Затем VueJS обновляет DOM недавно добавленной цитатой.

Метод addQuote() работает следующим образом:

  1. Сначала присоедините функцию обратного вызова / прослушиватель к событию 'value' в quotesRef
quotesRef.on('value', function (snapshot) {
   ....
})

  1. Далее создается ссылка на базу данных (дочерний элемент quotesRef) с идентификатором this.newQuoteIdx

    var newQuoteRef = quotesRef.child(this.newQuoteIdx)

Затем вызывается set() (на этом вновь созданном Ref), добавляющее новую цитату в базу данных RealTime Firebase.

  1. value событие запускается (присоединяется с шага 1) и вызывается прослушиватель / обратный вызов.

Обратный вызов ищет ключ этой новой цитаты в существующем списке элементов, сопоставляя ключи localArr, и snap.key, если не найден, добавляет новую цитату в localArr. localArr фиксирует в хранилище vuex.

`vm.$store.commit('set_allQuotes', localArr)`

Затем VueX обновляет все компоненты подписчика этого массива. Затем VueJS добавляет новую цитату в существующий список цитат (обновляет DOM)

При отладке метода addQuote я заметил проблему, при которой выполнение / поток сценария (через F8 в chrome-отладчике) сначала входит в слушатель / обратный вызов, присоединенный к событию value, перед кодом newQuoteRef.set({ ... }), который добавляет новая цитата (на базе Firebase), которая, в свою очередь, вызовет событие 'value'.

Я не уверен, почему это происходит. Может ли кто-нибудь объяснить, почему слушатель / обратный вызов вызывается до создания кавычек.

Кэшируются ли дочерние узлы (из QuotesRef) на стороне клиента, так что значение запускается даже до добавления новой цитаты.

Спасибо

1 Ответ

1 голос
/ 25 мая 2019

Если я правильно понимаю ваш вопрос (ваш код не очень прост для подражания! :-)), это нормальное поведение. Как объяснено в документации :

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

Демонстрация вашей песочницы на самом деле не показывает, как работает приложение, но обычно вы не должны настраивать слушателя в методе, который сохраняет новый узел в базе данных. Эти две вещи должны быть отделены.

Одним из распространенных подходов является установка прослушивателя в created ловушке компонента (см. https://vuejs.org/v2/guide/instance.html#Instance-Lifecycle-Hooks и https://vuejs.org/v2/api/#created), а затем в вашем методе addQuote, который вы просто записываете в базу данных. Как только вы напишите, слушатель будет уволен.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...