Skip to main content

Grouped Load on Demand

The GroupedVirtuoso component can have a Footer, that has a "load more" button that appends more items to the existing ones.

To add additional items to the groups, you should re-calculate the groupCounts property value with the group values of the newly loaded items. Check the source code of this example for an example implementation.

The calculateGroupsSoFar Slices the total groups into the groups which contain the items so far. For example, if you have [10, 10, 10, 10] groups in total, slicing them to 23 will result in [10, 10, 3].

The setTimeout delay is just for illustrative purposes - in reality, the data will be fetched from a remote source.

Live Editor
function App() {
  const { users, groups, groupCounts } = useMemo(() => {
    const letters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'.split('')
    
    const users = letters.flatMap((letter) => {
      return Array.from({ length: 20 }, (_, index) => ({
        name: `${letter} User ${index}`,
        initials: `${letter}${index}`,
        description: `Description for user ${index}`,
      }))
    })    

    const groups = Array.from({ length: 10 }, (_, index) => { return letters[index] })

    const groupCounts = groups.map((letter, index) => {
      return users.filter((user, userIndex) => user.name.startsWith(letter)).length
    })
    return { users, groups, groupCounts }
  }, [])

  const calculateGroupsSoFar = useCallback((totalGroups, count) => {
    const groups = []
    let i = 0
    do {
      const group = totalGroups[i]
      groups.push(Math.min(group, count))
      count -= group
      i++
    } while (count > 0 && i <= totalGroups.length)
    return groups
  }, [])

  const [currentGroupCounts, setCurrentGroupCounts] = useState([])
  const loadedItems = useRef(0)
  const [loading, setLoading] = useState(false)

  const loadMore = useCallback(() => {
    setLoading(true)
    return setTimeout(() => {
      loadedItems.current += 50
      setLoading(false)
      setCurrentGroupCounts(
        calculateGroupsSoFar(groupCounts, loadedItems.current)
      )
    }, 500)
  }, [])

  useEffect(() => {
    const timeoutRef = loadMore()
    return () => clearTimeout(timeoutRef)
  }, [])

  return (
    <GroupedVirtuoso
      style={{ height: 400 }}
      groupCounts={currentGroupCounts}
      groupContent={index => (
        <div style={{ backgroundColor: 'var(--ifm-color-content-inverse)', paddingTop: '1rem' }}>Group {groups[index]}</div>
      )}
      itemContent={index => (
        <div>{users[index].name}</div>
      )}
      components={{
        Footer: () => {
          return (
            <div
              style={{
                padding: '2rem',
                display: 'flex',
                justifyContent: 'center',
              }}
            >
              <button disabled={loading} onClick={loadMore}>
                {loading ? 'Loading...' : 'Press to load more'}
              </button>
            </div>
          )
        },
      }}
    />
  )
}
Result
Loading...