Virtualization (React Window)

Loading

Virtualization (or windowing) is a technique to efficiently render large lists by only displaying the items currently visible in the viewport. React Window is a popular library for implementing virtualization in React applications.

Why Use Virtualization?

  • Performance: Renders only visible items instead of the entire list
  • Memory Efficiency: Reduces DOM node count
  • Smooth Scrolling: Maintains performance with large datasets
  • Better UX: Prevents jank and lag with long lists

React Window Basics

Installation

npm install react-window
# or
yarn add react-window

Core Components

  1. FixedSizeList: For lists with items of the same size
  2. VariableSizeList: For lists with items of varying sizes
  3. FixedSizeGrid: For 2D grids with fixed cell sizes
  4. VariableSizeGrid: For 2D grids with variable cell sizes

Basic Example (FixedSizeList)

import { FixedSizeList as List } from 'react-window';

const Row = ({ index, style }) => (
  <div style={style}>
    Row {index}
  </div>
);

const Example = () => (
  <List
    height={500}
    itemCount={1000}
    itemSize={35}
    width={300}
  >
    {Row}
  </List>
);

Variable Size List

import { VariableSizeList as List } from 'react-window';

const rowHeights = new Array(1000)
  .fill(true)
  .map(() => 25 + Math.round(Math.random() * 50));

const Row = ({ index, style }) => (
  <div style={style}>
    Row {index} (height: {rowHeights[index]}px)
  </div>
);

const Example = () => (
  <List
    height={500}
    itemCount={1000}
    itemSize={index => rowHeights[index]}
    width={300}
  >
    {Row}
  </List>
);

Dynamic Content Loading

Combine with lazy loading for large datasets:

import { FixedSizeList as List } from 'react-window';

const Row = ({ index, style, data }) => (
  <div style={style}>
    {data[index].name}
  </div>
);

const Example = ({ items }) => (
  <List
    height={500}
    itemCount={items.length}
    itemSize={35}
    width={300}
    itemData={items}
  >
    {Row}
  </List>
);

Grid Example

import { FixedSizeGrid as Grid } from 'react-window';

const Cell = ({ columnIndex, rowIndex, style }) => (
  <div style={style}>
    Item {rowIndex},{columnIndex}
  </div>
);

const Example = () => (
  <Grid
    columnCount={1000}
    columnWidth={100}
    height={500}
    rowCount={1000}
    rowHeight={35}
    width={800}
  >
    {Cell}
  </Grid>
);

Performance Tips

  1. Memoize row components: Prevent unnecessary re-renders
  2. Use CSS transforms: For smoother animations
  3. Avoid inline styles: Extract to CSS classes when possible
  4. Implement shouldComponentUpdate: For complex row components
const Row = React.memo(({ index, style, data }) => {
  // Your row implementation
});

Advanced Techniques

Infinite Loading

Combine with libraries like react-window-infinite-loader:

import InfiniteLoader from 'react-window-infinite-loader';
import { FixedSizeList as List } from 'react-window';

const Example = ({ hasNextPage, items, loadMore }) => {
  const itemCount = hasNextPage ? items.length + 1 : items.length;

  const loadMoreItems = isNextPageLoading ? () => {} : loadMore;

  const isItemLoaded = index => !hasNextPage || index < items.length;

  const Row = ({ index, style }) => {
    if (!isItemLoaded(index)) {
      return <div style={style}>Loading...</div>;
    }
    return <div style={style}>{items[index]}</div>;
  };

  return (
    <InfiniteLoader
      isItemLoaded={isItemLoaded}
      itemCount={itemCount}
      loadMoreItems={loadMoreItems}
    >
      {({ onItemsRendered, ref }) => (
        <List
          height={500}
          itemCount={itemCount}
          itemSize={35}
          onItemsRendered={onItemsRendered}
          ref={ref}
          width={300}
        >
          {Row}
        </List>
      )}
    </InfiniteLoader>
  );
};

Custom Scrollbars

Use react-window with libraries like react-custom-scrollbars:

import { List } from 'react-window';
import { Scrollbars } from 'react-custom-scrollbars';

const CustomScrollbars = ({ onScroll, forwardedRef, style, children }) => {
  const refSetter = React.useCallback(scrollbarsRef => {
    if (scrollbarsRef) {
      forwardedRef(scrollbarsRef.view);
    } else {
      forwardedRef(null);
    }
  }, [forwardedRef]);

  return (
    <Scrollbars
      ref={refSetter}
      style={{ ...style, overflow: 'hidden' }}
      onScroll={onScroll}
    >
      {children}
    </Scrollbars>
  );
};

const CustomScrollbarsVirtualList = React.forwardRef((props, ref) => (
  <CustomScrollbars {...props} forwardedRef={ref} />
));

const Example = () => (
  <List
    height={500}
    itemCount={1000}
    itemSize={35}
    width={300}
    outerElementType={CustomScrollbarsVirtualList}
  >
    {Row}
  </List>
);

When to Use React Window

  • Lists with 100+ items
  • Grids with large numbers of cells
  • Data tables with many rows
  • Any scrollable content that might impact performance

React Window is more lightweight than its predecessor (React Virtualized) and is typically the better choice for new projects.

Leave a Reply

Your email address will not be published. Required fields are marked *