A React component that can virtualise lists and any set of children
virtual-window
A list virtualiser that can create virtual rows out of arrays or a total count, or virtualise an arbitrary set of React components.
Installation
npm i virtual-window
Usage
You can place a Virtual Window over a set of arbitrary React components by simply wrapping them
function MyComponent({ list }) {
  return (
    <VirtualWindow>
      <MyComponent1 />
      {list.map(l => (
        <SomeComponent key={l.id} data={l} />
      ))}
      <MyLastComponent />
    </VirtualWindow>
  )
}
You can also use the mode where it is a virtual list, in this mode you specify a number of parameters
| Parameter | Default | Purpose | 
|---|---|---|
| className | A class to apply to the outer wrapper component | |
| item | <Simple/>a fragment like component | The item that will be used to display this element. This component is passed properties for the item being displayed and the indexof it.e.g. <VirtualWindow list={items} item={<MyComponent withAny={prop}/>} /> | 
| itemSize | 36 | The default size to expect for items | 
| keyFn | WeakMap of items to unique integers | A function to create a key for an item in the list. e.g. <VirtualWindow list={items} keyFn={v=>v.id}/> | 
| list | The array of items to display | |
| pass | item | a string containing the name of the property to pass to the itembeing rendered. | 
| onConfigure | A callback function that receives properties of the Virtual Window in attributes called expectedSizeandscrollingElement. The callback is triggered whenever measurement detects a change in the expected size of items. | |
| onVisibleChanged | A callback function that receives the first and last visible items as they change onVisibleChanged={(first, last)=>console.log(first, last)}.You can use this property to update the list and provide endless scrolling. | |
| overscan | 2 | The number of additional pages to render below and above the visible list for sizing | 
| totalCount | The number of records to render, this is used instead of a listto have the component totally virtual. In this case theitempassed to the rendered component is the index to use for the data. | 
Sizing
By default the virtual item container has a height of 100% and a flex of 1. This allows it to resize into
various useful containers. If you need to specify a height then either size the wrapping component or pass a className
to the <VirtualWindow/>
Example
import { VirtualWindow } from "lib/VirtualWindow"
import { Box, IconButton } from "@material-ui/core"
import { useState, useMemo } from "react"
import { MdExpandLess, MdExpandMore } from "react-icons/md"
import randomColor from "randomcolor"
import { routes } from "./routes"
import { makeStyles } from "@material-ui/core"
const useStyles = makeStyles({
  virtualBox: {
    height: 370,
    background: "#0002",
    overflow: "auto"
  }
})
export const items = Array.from({ length: 2000 }, (_, i) => ({
  content: i,
  color: randomColor()
}))
export default function App() {
  const classes = useStyles()
  return (
    <div className="App">
      <div className={classes.virtualBox}>
        <VirtualWindow list={items} item={<DummyItem />} />
      </div>
    </div>
  )
}
export function DummyItem({ item, index }) {
  const [extra, setExtra] = useState(item.upsized || 0)
  item.upsized = extra
  const style = useMemo(
    () => ({
      minHeight: 34 + (index & 7) * 9 + extra,
      width: "100%",
      background: item.color
    }),
    [item.color, extra, index]
  )
  return (
    <Box display="flex" flexDirection="row" p={2} style={style}>
      <Box flex={1} />
      <Box
        borderRadius={4}
        bgcolor="#ffffffdc"
        color="#444"
        boxShadow="inset 0 0 6px 0px #000c"
        p={1}
        display="flex"
        alignItems="center"
      >
        <Box mr={2}>{item.content}</Box>
        <Box>{JSON.stringify(style, null, 2)}</Box>
        <Box
          ml={1}
          onClick={() => {
            if (extra) {
              setExtra(0)
            } else {
              setExtra(Math.floor(Math.random() * 90) + 20)
            }
          }}
        >
          <IconButton color="primary">
            {extra ? <MdExpandLess /> : <MdExpandMore />}
          </IconButton>
        </Box>
      </Box>
      <Box flex={1} />
    </Box>
  )
}