Skip to content

Instantly share code, notes, and snippets.

@bvaughn
Last active April 12, 2022 15:37
Show Gist options
  • Save bvaughn/39ee223c7503a834afc1c2e94df27fc5 to your computer and use it in GitHub Desktop.
Save bvaughn/39ee223c7503a834afc1c2e94df27fc5 to your computer and use it in GitHub Desktop.
react-window itemData -> data props behavior

Related discussion on bvaughn/react-window/issues/85.

Overview

The specific API feature this Gist is exploring is the itemData prop. This prop provides a way for a component to pass "contextual" list data to an item renderer without adding the overhead of using context. In most cases, a single value is passed (e.g. an array/list) like so:

function ComponentThatRendersAListOfItems({ itemsArray, ...rest }) {
  render() {
    // Pass items array to the item renderer component as itemData:
    return (
      <FixedSizeList itemData={itemsArray} {...rest}>
        {ItemRenderer}
      </FixedSizeList>
    );
  }
}
 
// The item renderer is declared outside of the list-rendering component.
// So it has no way to directly access the items array.
function ItemRenderer({ data, index, style }) {
  // Access the items array using the "data" prop:
  const item = data[index];

  return (
    <div style={style}>
      {item.name}
    </div>
  );
}

But in some advanced cases, multiple values may be required– in which case a wrapper object can be passed.

react-window currently passes this wrapper object to item renderers as the data prop (as shown below) but this has implications on memoization (e.g. shouldComponentUpdate, React.memo) since changes to this wrapper item would cause a component to re-render unless the object itself is properly memoized (as shown in the react-window docs example).

react-window could spread the itemData object and pass individual props (as shown in this example) in order to relax this memoization requirement, but this would have the following drawbacks:

  • The common use case of passing a single value (e.g. an array or list of items) would require a wrapper object to be passed (e.g. <List itemData={itemsArray} {...rest} /> would become <List itemData={{itemsArray}} {...rest} />).
  • This could lead to naming conflicts if your itemData prop defined an attribute that List itself reserved (e.g. index, style).
  • Related to the above drawback, newly added item renderer props would become backwards breaking changes, since any new prop may clash with a pre-existing user-defined itemData prop. This could lead to unnecessary version fragmentation.
import { FixedSizeList as List } from 'react-window';
// Given a List with an itemData object like so:
<List
height={height}
itemCount={itemsArray.length}
itemData={{
itemsArray,
addItemFn,
removeItemFn
}}
itemSize={35}
width={width}
>
{Row}
</List>
function Row({ data, index, style }) {
const { itemsArray, addItemFn, removeItemFn } = data;
const item = itemsArray[index];
return (
<div style={style}>
{/* ... */}
</div>
);
}
import { FixedSizeList as List } from 'react-window';
// Given a List with an itemData object like so:
<List
height={height}
itemCount={itemsArray.length}
itemData={{
itemsArray,
addItemFn,
removeItemFn
}}
itemSize={35}
width={width}
>
{Row}
</List>
function Row({ addItemFn, index, itemsArray, removeItemFn, style }) {
const item = itemsArray[index];
return (
<div style={style}>
{/* ... */}
</div>
);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment