# bestyled/flatlist

A performant, infinite, virtualized list for React. See `react-virtualized`, `react-window` and `react-tiny-virtual-list` for prior art; this package is designed to

- Be extremely fast, with fine grained use of React Hooks (16.8+) instead of the less granular PureComponent used by existing solutions
- Delegate absolution positioning to the browser with use of FlexBox layout (removes need for `top` / `size` styles on every element, as we simply observe what the browser does and then size the outer container accordingly)
- Assumes variable size vertical items by default, with auto-resizing whenever the layout changes; if you want a FixedSizeList or Grid you may want to stick with `react-window`
- Avoid dependencies on CellMeasurer and other techniques to pre-determine height of every item and instead build right in using a very simple react hook for ResizeObserver (polyfilled using react-resize-observer)
- Support scrolling to Index without calculating the size of all intermediate entries, particularly useful for Scroll-To-End based conversational user interfaces
- Removes advanced APIs like sticky index and sticks to the absolute minimum required to run a variable dynamic list

Unlike just about all other implemetations of virtualized lists for React, no old-school React Components inside, with just one code file for the Functional Component and one file for the Size and Position state manager.

## Example Usage

```ts
import React from 'react'
import { render } from 'react-dom'
import { FlatList } from '@bestyled/contrib-flatlist'
import { AutoSizer } from '@bestyled/contrib-common'

// kick off the smoothscroll polyfill (for Safari, etc.)
import smoothscroll from 'smoothscroll-polyfill'
smoothscroll.polyfill()

const listItems = items.map((item, index) => (
  <React.Fragment key={index}>
    <RowHeader date={item.header} />
    <RowCard item={item} />
  </React.Fragment>
))

const renderRow = ({ index }) => {
  return listItems[index]
}

render(
  <AutoSizer flex={1} my={3}>
    {({ height }) => (
      <List
        estimatedItemSize={100}
        height={height}
        itemCount={listItems.length}
        scrollToIndex={listItems.length - 1}
        scrollToAlignment="end"
        renderItem={renderRow}
      />
    )}
  </AutoSizer>
)
```

### Prop Types

| Property          | Type               | Required? | Description                                                                                                                                                                                                                                                                                                                                                                                                                           |
| :---------------- | :----------------- | :-------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| height            | Number \| String\* | ✓         | Height of List. This property will determine the number of rendered items.                                                                                                                                                                                                                                                                                                                                                            |
| itemCount         | Number             | ✓         | The number of items you want to render                                                                                                                                                                                                                                                                                                                                                                                                |
| renderItem        | Function           | ✓         | Responsible for rendering an item given it's index: `({index: number}): React.PropTypes.node`. The returned element must handle the given item key; any styling is up to you.                                                                                                                                                                                                                                                         |
| scrollToIndex     | Number             |           | Item index to scroll to (by forcefully scrolling if necessary) x                                                                                                                                                                                                                                                                                                                                                                      |
| scrollToAlignment | String             |           | Used in combination with `scrollToIndex`, this prop controls the alignment of the scrolled to item. One of: `'start'`, `'center'`, `'end'` or `'auto'`. Use `'start'` to always align items to the top of the container and `'end'` to align them bottom. Use `'center`' to align them in the middle of the container. `'auto'` scrolls the least amount possible to ensure that the specified `scrollToIndex` item is fully visible. |
| stickyIndices     | Number[]           |           | An array of indexes (eg. `[0, 10, 25, 30]`) to make certain items in the list sticky (`position: sticky`)                                                                                                                                                                                                                                                                                                                             |
| overscanCount     | Number             |           | Number of extra buffer items to render above/below the visible items. Tweaking this can help reduce scroll flickering on certain browsers/devices.                                                                                                                                                                                                                                                                                    |
| estimatedItemSize | Number             |           | Used to estimate the total size of the list before all of its items have actually been measured. The estimated total height is progressively adjusted as items are rendered.                                                                                                                                                                                                                                                          |
| onScroll          | Function           |           | Callback invoked whenever the scroll offset changes within the inner scrollable region. It has the following signature: `(scrollTop: number, event: React.UIEvent<HTMLDivElement>)`.                                                                                                                                                                                                                                                  |

## Dependencies

Three standalone React Hooks are used from `@bestyled/contrib-common`. These could easily be incorporated yourself to eliminate this dependency. One of these has an additional depdency on `ResizeObserver` polyfill

- useResizeObserver - simple wrapper around ResizeObserver to be notified whenever an element size has been changed
- useForceUpdate - convenience method to force a state change
- useInstanceRef - convenience method to lazy initialize an instance reference

Peer dependency on `smoothscroll-polyfill` and `react`

## License

Apache 2.0
