I have a virtual list using infinite scroll in my React app, and I want to use list groups to group items in the list by date.
I have everything working, except that the virtual list only counts items in the first array (of grouped items), meaning that when it hits the last element in the list (say, the 9th group) it rerenders with a fromIndex of 10 and a toIndex of 9, and gets stuck in a loop or rerendering.
Is there a way of getting virtual lists to recognise groups so that it knows how many actual elements there are in total?
My code:
// @flow
import * as React from 'react'
import PropTypes from 'prop-types'
import {
Page, NavRight, List, ListItem,
Navbar, Link, Preloader, ListGroup,
Searchbar, f7
} from 'framework7-react'
import { isEmpty } from 'lodash'
import { useTranslation } from 'react-i18next'
import { useSelector, useDispatch } from 'react-redux'
import { DateTime } from 'luxon'
import { loadJobHistory } from 'ducks/jobs'
type Props = {
}
let virtualList = {}
const JobHistory = (props: Props): React.Node => {
const { t } = useTranslation()
const dispatch = useDispatch()
const driverID = useSelector(state => state.driver.driverObj.id)
const jobHistoryByDate = useSelector(state => state.jobs.jobHistoryByDate)
React.useEffect(() => {
dispatch(loadJobHistory(driverID))
}, [])
const [vlData, setVLData] = React.useState({ items: [], topPosition: 0 })
const [allowInfinite, setAllowInfinite] = React.useState(true)
const [showPreloader, setShowPreloader] = React.useState(true)
const [isSearching, setIsSearching] = React.useState(false)
React.useEffect(() => {
if (!isEmpty(virtualList) && !isSearching) {
virtualList.replaceAllItems(jobHistoryByDate)
setTimeout(() => setAllowInfinite(true), 1000)
}
}, [jobHistoryByDate])
function searchAll (query, items) {
const found = []
for (let i = 0; i < items.length; i += 1) {
if (items[i].pickupAddress.toLowerCase().indexOf(query.toLowerCase()) >= 0 ||
items[i].destinationAddress.toLowerCase().indexOf(query.toLowerCase()) >= 0 ||
query.trim() === '') {
found.push(i)
}
}
return found // return array with matched indexes
}
const renderExternal = (vl, vlData) => {
virtualList = vl
setVLData(vlData)
}
function loadMore () {
if (!allowInfinite)
{ return }
setAllowInfinite(false)
dispatch(loadJobHistory(driverID, true))
}
return (
<Page
infinite
infiniteDistance={100}
infinitePreloader={showPreloader}
onInfinite={loadMore}>
<Navbar title='Job History' backLink='Back'>
<NavRight>
<Link searchbarEnable='.searchbar' iconIos='f7:search' iconAurora='f7:search' iconMd='material:search' />
</NavRight>
<Searchbar
className='searchbar'
expandable
searchContainer='.virtual-list'
searchItem='li'
searchIn='.item-content'
disableButton={!f7.theme.aurora}
onSearchbarDisable={(searchbar) => {
setIsSearching(false)
setAllowInfinite(true)
setShowPreloader(true)
}}
onSearchbarEnable={(searchbar) => {
setIsSearching(true)
setAllowInfinite(false)
setShowPreloader(false)
}}
/>
</Navbar>
<List className='searchbar-not-found'>
<ListItem title={t('SearchNothingFound', 'Nothing found')} />
</List>
<List
className='searchbar-found'
mediaList
virtualList
virtualListParams={{ items: jobHistoryByDate, createUl: false, searchAll: searchAll, renderExternal: renderExternal, height: (f7.theme.ios ? 112 : (f7.theme.md ? 124 : 112)) }}
>
{vlData.items.length > 1 && vlData.items.map((group, index) => {
const { date } = group
return (
<ListGroup mediaList key={index}>
<ListItem title={DateTime.fromISO(date).toLocaleString()} key={date} groupTitle mediaItem/>
{
group.items.map((item, index) => {
const { startTime, pickupAddress, destinationAddress, fare } = item
const jobStartTime = new Date(Date.parse(startTime))
return (
<ListItem
key={item.id}
mediaItem
chevronCenter
link='#'
title={t('jobStartTime', { date: jobStartTime })}
after={t('jobStartTimeDate', { date: jobStartTime })}
style={{ top: `${vlData.topPosition}px` }}
>
<div slot='inner'>
<div className='job-history-address'>{t('jobFromLabel')} {pickupAddress}</div>
<div className='job-history-address'>{t('jobToLabel')} {destinationAddress}</div>
<div>£{fare}</div>
</div>
</ListItem>
)})
}
</ListGroup>
)})
}
</List>
</Page>
)
}
JobHistory.propTypes = ({
}: {...})
export default JobHistory