Skip to main content

One post tagged with "react-window"

View All Tags

From react-window to react-virtual

ยท 3 min read

The tremendous Tanner Linsley recently released react-virtual. react-virtual provides "hooks for virtualizing scrollable elements in React".

I was already using the (also excellent) react-window for this purpose. react-window does the virtualising job and does it very well indeed However, I was both intrigued by the lure of the new shiny thing. I've also never been the biggest fan of react-window's API. So I tried switching over from react-window to react-virtual as an experiment. To my delight, the experiment went so well I didn't look back!

What did I get out of the switch?

  • Simpler code / nicer developer ergonomics. The API for react-virtual allowed me to simplify my code and lose a layer of components.
  • TypeScript support in the box
  • Improved perceived performance. I didn't run any specific tests to quantify this, but I can say that the same functionality now feels snappier.

I tweeted my delight at this and Tanner asked if there was commit diff I could share. I couldn't as it's a private codebase, but I thought it could form the basis of a blogpost.

Nice! Do you have a commit diff we could see?

โ€” Tanner Linsley โš›๏ธ (@tannerlinsley) May 10, 2020

In case you hadn't guessed, this is that blog post...

Make that change#

So what does the change look like? Well first remove react-window from your project:

yarn remove react-window @types/react-window

Add the dependency to react-virtual:

yarn add react-virtual

Change your imports from:

import { FixedSizeList, ListChildComponentProps } from 'react-window';

to:

import { useVirtual } from 'react-virtual';

Change your component code from:

type ImportantDataListProps = {    classes: ReturnType<typeof useStyles>;    importants: ImportantData[];};
const ImportantDataList: React.FC<ImportantDataListProps> = React.memo(props => (    <FixedSizeList        height={400}        width={'100%'}        itemSize={80}        itemCount={props.importants.length}        itemData={props}    >        {RenderRow}    </FixedSizeList>));
type ListItemProps = {    classes: ReturnType<typeof useStyles>;    importants: ImportantData[];};
function RenderRow(props: ListChildComponentProps) {    const { index, style } = props;    const { importants, classes } = props.data as ListItemProps;    const important = importants[index];
    return (        <ListItem button style={style} key={index}>            <ImportantThing classes={classes} important={important} />        </ListItem>    );}

Of the above you can delete the ListItemProps type and the associate RenderRow function. You won't need them again! There's no longer a need to pass down data to the child element and then extract it for usage; it all comes down into a single simpler component.

Replace the ImportantDataList component with this:

const ImportantDataList: React.FC<ImportantDataListProps> = React.memo(props => {    const parentRef = React.useRef<HTMLDivElement>(null);
    const rowVirtualizer = useVirtual({        size: props.importants.length,        parentRef,        estimateSize: React.useCallback(() => 80, []), // This is just a best guess        overscan: 5    });
    return (            <div                ref={parentRef}                style={{                    width: `100%`,                    height: `500px`,                    overflow: 'auto'                }}            >                <div                    style={{                        height: `${rowVirtualizer.totalSize}px`,                        width: '100%',                        position: 'relative'                    }}                >                    {rowVirtualizer.virtualItems.map(virtualRow => (                        <div                            key={virtualRow.index}                            ref={virtualRow.measureRef}                            className={props.classes.hoverRow}                            style={{                                position: 'absolute',                                top: 0,                                left: 0,                                width: '100%',                                height: `${virtualRow.size}px`,                                transform: `translateY(${virtualRow.start}px)`                            }}                        >                            <ImportantThing                                classes={props.classes}                                important={props.importants[virtualRow.index]}                            />                        </div>                    ))}                </div>            </div>    );});

And you are done! Thanks Tanner for this tremendous library!