Infinite Scroller Loses Reactivity when Adding New Data after Initial Loading of Data



  • I am trying to use quasar infinite-scroll to enhance rendering time with large amounts of data in my website. I am querying data from a sever and sorting that data into tables. The tables are then put into cards. Each card can have up to 100 rows of data with up to 10 columns. Additionally, the card heights are variable and the number of cards are also variable. Finally, users can change information that is fed into the card generation or table information thus I need the infinite scroll to “restart” whenever one of these variables change.

    So far I have a “working” state of the infinite scroll meaning that I see that not all the data is loaded at once. The part I need help on is the reactivity portion. The content of the web page does not refresh whenever one of the variables are updated from the user side. Please let me know if these things don’t make sense

    I should add that I the website was reactive when the cards weren’t wrapped around the q-infinite-scroll component. I just took a huge hit on rendering time since everything was trying to be loaded at once.

    I am also open to suggestions on other methods of enhancing rendering

    <q-infinite-scroll
          class="col-12 justify-center"
          :handler="loadMore"
          :offset="40"
        >
          <div class="row col-12 shadow-1 q-mt-md q-pa-lg results-card"
            v-for="(item, index) in items" :key="index"
            ref="collate"
          >
            <!-- check title -->
            <div
              v-if="searchText"
              class="col-12 text-weight-light uppercase"
              v-html="highlight(item)"
            />
            <div v-else
              class="col-12 text-weight-light uppercase"
            >
              {{item.target}}
            </div>
            <!-- collation table -->
            <results-collate-table
              dense
              class="col-12 no-shadow"
              :visibleColumns="[...currentColumnPreset.selections, ...nonCommonColumnNames]"
              :items="orderItems(rolledItems.map[item.target])"
            />
          </div>
          <q-spinner-dots
            class="offset-sm-6"
            color="primary"
            :size="40"
            v-if="!turnOff">
          </q-spinner-dots>
        </q-infinite-scroll>
    
    
    methods: {
    loadMore (index, done) {
          // If there is more than 1 dispo loaded, then add a delay to the rendering
          if (this.selectedPendingItems.length + this.selectedApprovedItems.length === 1) {
            this.delay = 0
          }
          // Mechanism for loading more violations
          if (this.turnOff === false) {
            setTimeout(() => {
              let items = []
              // Load violations in steps of itemLoad
              for (var i = this.idx; i < this.idx + this.itemLoad; i++) {
                if (i < this.filteredCheckNames.length) {
                  items.push(this.filteredCheckNames[i])
                } else {
                  this.turnOff = true
                }
              }
              this.idx = i
              this.items = this.items.concat(items)
              done()
            }, this.delay)
          }
        },
    

    filteredchecknames is an object of strings that are keys to see what data to load
    this is what users choose



  • Hi Carlos, can you show all the code in the <script> section of the component?

    Scott



  • <script>
    import { scroll } from 'quasar'
    const { getScrollTarget, setScrollPosition } = scroll
    import ResultsCollateTable from '../components/ResultsCollateTable.vue'
    import { mapState, mapGetters } from 'vuex'
    import fuzzysort from 'fuzzysort'
    
    export default {
      name: 'CollateResultsPage',
      components: {
        ResultsCollateTable
      },
      data () {
        return {
          items: [],
          idx: 0,
          turnOff: false,
          itemLoad: 3,
          delay: 1000
        }
      },
      computed: {
        ...mapState('appCompareDatas', ['searchText']),
        ...mapState('compareDatas', [
          'reviewItems',
          'selectedPendingItems',
          'selectedApprovedItems'
        ]),
        ...mapState('persistCompareDatas', [
          'autoColumns'
        ]),
        ...mapGetters('persistCompareDatas', [
          'currentColumnPreset',
          'currentRowPreset'
        ]),
        ...mapGetters('compareDatas', [
          'fetchItemsLoading',
          'haveReviewItems',
          'rollItems'
        ]),
        allItems () {
          return [
            ...this.selectedPendingItems,
            ...this.selectedApprovedItems
          ]
        },
        fieldRecords () {
          const items = this.allItems, fields = this.autoColumns
          const fieldSet = fields.reduce((o, f) => {
            items.forEach(i => {
              f.name in o ? o[f.name].add(i[f.name]) : o[f.name] = new Set([i[f.name]])
            })
            return o
          }, {})
          return fields.map(f => ({
            name: f.name,
            label: f.label,
            value: Array.from(fieldSet[f.name])
          }))
        },
        commonTags () {
          return this.fieldRecords.filter(r => r.value.length === 1)
            .map(r => ({...r, value: r.value[0]}))
        },
        nonCommonTags () {
          return this.fieldRecords.filter(r => r.value.length !== 1)
        },
        nonCommonColumnNames () {
          return this.nonCommonTags.map(f => f.name)
        },
        rolledItems () {
          return this.rollItems(this.allItems)
        },
        rolledCheckNames () {
          return this.rolledItems.checks
        },
        filteredRolledCheckNames () {
          let names = [...this.rolledCheckNames]
          if (this.currentRowPreset.value === 'hideZero') {
            names = names.filter(name => (this.groupVioCount(name) > 0))
          } else if (this.currentRowPreset.value === 'hidePendingZero') {
            names = names.filter(name => (this.groupPendingVioCount(name) > 0))
          } else if (this.currentRowPreset.value === 'showOnlyZero') {
            names = names.filter(name => (this.groupVioCount(name) === 0))
          }
          return names
        },
        rolledTargets () {
          return this.filteredRolledCheckNames.map(t => fuzzysort.prepare(t))
        },
        filteredCheckNames () {
          return !this.searchText
            ? this.filteredRolledCheckNames.map(name => ({target: name}))
            : fuzzysort.go(this.searchText, this.rolledTargets, {
              threshold: -10000,
              allowTypo: true,
              limit: 200
            })
        },
        showing () {
          return `${this.filteredCheckNames.length}/${this.rolledCheckNames.length}`
        }
      },
      methods: {
        loadMore (index, done) {
          // If there is more than 1 Data loaded, then add a delay to the rendering
          if (this.selectedPendingItems.length + this.selectedApprovedItems.length === 1) {
            this.delay = 0
          }
          // Mechanism for loading more violations
          if (this.turnOff === false) {
            setTimeout(() => {
              let items = []
              // Load violations in steps of itemLoad
              for (var i = this.idx; i < this.idx + this.itemLoad; i++) {
                if (i < this.filteredCheckNames.length) {
                  items.push(this.filteredCheckNames[i])
                } else {
                  this.turnOff = true
                }
              }
              this.idx = i
              this.items = this.items.concat(items)
              done()
            }, this.delay)
          }
        },
        orderItems (resultsMap) {
          return this.rolledItems.ids
            .filter(id => id in resultsMap)
            .map(id => resultsMap[id])
        },
        highlight (result) {
          return result ? fuzzysort.highlight(result, '<span class="text-primary">', '</span>') : ''
        },
        groupVioCount (groupName) {
          const group = this.rolledItems.map[groupName]
          const oids = Object.keys(group)
          return oids.map(id => group[id])
            .map(item => item.count)
            .reduce((a, c) => a + c, 0)
        },
        groupPendingVioCount (groupName) {
          const group = this.rolledItems.map[groupName]
          const oids = Object.keys(group)
          return oids.map(id => group[id])
            .filter(item => item.scope === 'pending')
            .map(item => item.count)
            .reduce((a, c) => a + c, 0)
        },
        detectColumnPreset () {
          const flaggedChecks = !this.searchText
            ? this.filteredRolledCheckNames.map(name => ({target: name}))
            : fuzzysort.go(this.searchText, this.rolledTargets, {
              threshold: -10000,
              allowTypo: true,
              limit: 1
            })
          flaggedChecks[0].target = flaggedChecks[0].target.startsWith('/') ? flaggedChecks[0].target.substring(1) : flaggedChecks[0].target
          const numberElements = flaggedChecks[0].target.split('/')
          return numberElements.length > 1
            ? 'test1' : 'test2'
        },
        scrollToElement (el) {
          let target = getScrollTarget(el)
          let offset = el.offsetTop - el.scrollHeight
          let duration = 1000
          setScrollPosition(target, offset, duration)
        }
      },
      filters: {
        sanitizedTagValues: v => v.toUpperCase()
      },
      mounted () {
        this.currentColumnPreset.value = this.detectColumnPreset()
      }
    }
    </script>
    


  • Not sure, but I think it’s the concat of array items. I don’t think that will work.

    https://vuejs.org/v2/guide/list.html#Mutation-Methods

    Scott



  • I tried using push on this.items instead and I still have the same issue



  • I know I’ve already asked several times but is there a way to programatically restart the q-infinite-scroll? I was thinking that whenever the user changes one of the variables to just call this function. That way it would just do it properly.



  • That shouldn’t matter. Once the data is reactive, it stays reactive.Try something smaller. Smaller data set smaller everything and see if you can get it going with the thought pattern you have. It will be easier to reason about and in the end, you might find your solution.

    Scott



  • I still have not been able to get this one… I tried on very small sets of data to no avail



  • And with the information given, I can’t help much either.

    Scott