All Articles

react-windowを使いこなそう

サイトは大量の情報を表示したいケースがたくさんあります。

ストレートにループを使って全てを一度に表示しようとすると、全要素をレンダリングするコストを画面表示時に払うことになります。

そこで、react-windowを使うことで、効率よくレンダリングすることができるようになります。

react-windowを使おう

使用例

Fixed Size List

Variable Size List

Fixed Size Grid

Variable Size Grid

Scrolling indicators

Scrolling to an item

Memoized List items

RTL layout

COMPONENTS

FixedSizeList

FixedSizeListはリストに並べるアイテムコンポーネントのサイズ(水平方向リストの場合は幅・垂直方向リストの場合は高さ)が固定の場合に使用します。

PROPS

children: component

React component responsible for rendering the individual item specified by an index prop. This component also receives a style prop (used for positioning). If useIsScrolling is enabled for the list, the component also receives an additional isScrolling boolean prop. Function components are useful for rendering simple items:

<FixedSizeList {...props}>
  {({ index, style }) => (
    <div style={style}>
      Item {index}
    </div>
  )}
</FixedSizeList>

To render more complex items, you may wish to extend PureComponent to avoid unnecessary re-renders:

// Declare your item renderer outside of the render method:
class ItemRenderer extends PureComponent {
  render() {
    return (
      <div style={this.props.style}>
        Item {this.props.index}
      </div>
    );
  }
}

// Reference it inside of the render method:
<FixedSizeList {...props}>
  {ItemRenderer}
</FixedSizeList>
width: number | string

Listの幅を設定します。 水平方向リストの場合は、数値である必要があります。この値は一度にレンダリング・表示されるリストの数に影響を与えます。 垂直方向リストの場合は、数値または"50%"のような文字列を指定することができます。

height: number | string

Listの高さを設定します。 水平方向リストの場合は、数値または"50%"のような文字列を指定することができます。 垂直方向リストの場合は、数値である必要があります。この値は一度にレンダリングされるリストの数に影響を与えます。

itemCount: number

リストに表示するアイテムの総数を設定します。一度にいくつかのアイテムのみがレンダリングおよび表示されることに注意してください。

itemData: any

リストのアイテムとしてレンダリングするコンポーネントへ渡すデータを設定します。 この設定項目は、レンダリングするコンポーネントをクラスとして切り出しす時に使います。

  class ComponentThatRendersAListOfItems extends PureComponent {
    render() {
      // Pass items array to the item renderer component as itemData:
      return (
        <FixedSizeList
          itemData={this.props.itemsArray}
          {...otherListProps}
        >
          {ItemRenderer}
        </FixedSizeList>
      );
    }
  }

レンダリングするコンポーネントをクラスとして切り出しす時、直接データにアクセスすることができません。 しかし、itemDataでデータを渡すと、コンポーネントのprops#indexとprops#dataでアクセスすることができます。

  class ItemRenderer extends PureComponent {
    render() {
      // Access the items array using the "data" prop:
      const item = this.props.data[this.props.index];

      return (
        <div style={this.props.style}>
          {item.name}
        </div>
      );
    }
  }
initialScrollOffset: number = 0

Scroll offset for initial render.

For vertical lists, this affects scrollTop. For horizontal lists, this affects scrollLeft.

className: string = ""

一番外側の

要素に任意のCSS適用用のclassを追加します、

direction: string = "ltr"

テキストの方向とスクロールの方向を設定します。

  • ltr (default)
  • rtl このプロパティは、グリッドコンポーネント内のCSS direction style に自動的適用されます。
innerRef: function | createRef object

内部コンテナ要素へのRefを追加するための設定値です。

これはアドバンスプロパティです。

innerElementType: React$ElementType = "div"

内部コンテナ要素をタグ名で指定します。

これはアドバンスプロパティです。デフォルトではdivが使われます。

innerTagName: string

This property has been deprecated. Please use the innerElementType prop instead.

itemKey: function

デフォルトでは、リストはアイテムをキーとして使用します。 次の場合は問題ありません。

  • アイテムがソートされない。
  • アイテムが変更されない。
  • PureComponentを拡張したクラスを使用しない。
  • アイテムが状態を持っていない。 リストが上記の制約を満たさない場合は、itemKeyプロパティを使用して、アイテムに独自のキーを指定します。
function itemKey(index, data) {
  // Find the item at the specified index.
  // In this case "data" is an Array that was passed to List as "itemData".
  const item = data[index];

  // Return a value that uniquely identifies this item.
  // Typically this will be a UID of some sort.
  return item.id;
}
itemSize: number

アイテムのサイズを設定します。 垂直方向リストの場合は、高さを指定します。 水平方向リストの場合は、幅を指定します。

layout: string = "vertical"

Listの並べる方向を設定します。 下記の二つを設定することができます。

  • vertical : 垂直方向に並べます。上下方向にスクロールができます。
  • horizontal : 水平方向に並べます。左右方向にスクロールができます。 デフォルトはverticalとなっています。 Note that lists may scroll in both directions (depending on CSS) but content will only be windowed in the layout direction specified.
overscanCount: number = 1

現在、リストの可視エリアに表示しているアイテムの外側にあるアイテムをレンダリングする数を指定します。

このプロパティは下記の2点からとても重要です。

  • オーバースキャンは、タブキーによるフォーカス移動を可能にします。
  • オーバースキャンは、ユーザーが最初にスクロール開始時の空きスペースのフラッシュが減少、防止します。

大量のオーバースキャンをすると、パフォーマンスに悪影響を与えるので注意が必要です。 デフォルトでは、1つ分のアイテムをオーバースキャンします。

onItemsRendered: function

リストのレンダリング範囲が変更された時に呼ばれるコールバックメソッドを設定します。

コールバックの引数には各種変更内容が渡されます。

アイテムが他の理由(isScrollingやdata paramsの変更など)で再レンダリングされた場合は呼び出されません。

  • overscanStartIndex: オーバースキャン(リスト外で見えてはいない)開始要素番号
  • overscanStopIndex: オーバースキャン(リスト外で見えてはいない)停止要素番号
  • visibleStartIndex: 表示開始要素番号
  • visibleStopIndex: 表示停止要素番号
function onItemsRendered({
  overscanStartIndex,
  overscanStopIndex,
  visibleStartIndex,
  visibleStopIndex
}) {
  // All index params are numbers.
}
onScroll: function

スクロールポジションが変更された時に呼ばれるコールバックメソッドを設定します。

ユーザーのスクロールやscroll-toメソッドによって呼ばれます。

  • scrollDirection: スクロール方向
  • scrollOffset: スクロール量
  • scrollUpdateWasRequested: trueの場合、scrollTo()やscrollToItem()によって、コールバックが発生したことを表します。falseの場合は、ユーザー操作によるコールバックが発生したことを表します。
function onScroll({
  scrollDirection,
  scrollOffset,
  scrollUpdateWasRequested
}) {
  // scrollDirection is either "forward" or "backward".

  // scrollOffset is a number.

  // scrollUpdateWasRequested is a boolean.
  // This value is true if the scroll was caused by scrollTo() or scrollToItem(),
  // And false if it was the result of a user interaction in the browser.
}
useIsScrolling: boolean = false

リスト内に表示するアイテムコンポーネント・アイテム関数にisScrollingパラメータを渡すかどうかを設定します。 このパラメータを使ってアイテムをプレースホルダー表示することができます。 このパラメータを使う場合は、スクロールが停止したタイミング(isScrollingがtrueからfalseに変化したタイミング)で追加の描画処理が行われることに注意が必要です。

outerRef: function | createRef object

最外部のコンテナ要素にRefを設定します。

outerElementType: React$ElementType = "div"

最外部のコンテナ要素タイプをタグ名で指定します。 大抵の場合はdivが使用されます。

outerTagName: string

このプロパティは非推奨となっています。 最外部のコンテナ要素にタグ名を設定します。

style: Object = null

Optional inline style to attach to outermost

element.

METHODS

scrollTo(scrollOffset: number): void

スクロール量を指定して、リストをスクロールさせます。 scrollTopまたは、scrollLeftに値が設定されます。

scrollToItem(index: number, align: string = "auto"): void

アイテムの要素番号を指定してスクロールを行います。

デフォルトでは、指定されたアイテムが表示されるようにリストのスクロール量がなるべく少なくなるようになっています。 この挙動は、第二引数を指定することで変更することができます。

  • auto (default) : アイテムが表示される最小のスクロール量でスクロールを行います。既に表示されている場合はスクロールは発生しません。
  • smart : 既に表示されている場合はスクロールは発生しません。If it is less than one viewport away, scroll as little as possible so that it becomes visible. If it is more than one viewport away, scroll so that it is centered within the list.
  • center : アイテムがリストの中央に表示されるようにスクロールをします。
  • end : アイテムがリストの末尾に表示されるようにスクロールをします。(水平方向リストの場合は最右部・水平方向リストの場合は最下部)
  • start : アイテムがリストの先頭に表示されるようにスクロールをします。(水平方向リストの場合は最左部・水平方向リストの場合は最上部)

VariableSizeList

VariableSizeListはリストに並べるアイテムコンポーネントのサイズ(水平方向リストの場合は幅・垂直方向リストの場合は高さ)が可変の場合に使用します。

PROPS

設定できるpropsは、FixedSizeListで使えるpropsに加えて下記のpropsが使えるようになっています。

estimatedItemSize: number = 50

リストアイテムの推定サイズを設定します。

  • 垂直方向リストの場合は、リストアイテムの高さの推定値を指定したことになります。
  • 水平方向リストの場合は、リストアイテムの幅の推定値を指定したことになります。

この値は、全ての項目が推定される前に、リストの推定合計サイズを計算するために使われます。 合計サイズは、ユーザーのスクロール動作に影響し、新しいアイテムが測定されるたびに更新されます。

itemSize: (index: number) => number

リストアイテムのサイズを返却するためのコールバックメソッドを設定します。

FixedSizeListでは数値を直接設定していましたが、リストアイテムの要素番号を引数にもつコールバックメソッドを設定するように変更されているので注意が必要です。

  • 垂直方向リストの場合は、リストアイテムの高さを返却します。
  • 水平方向リストの場合は、リストアイテムの幅を返却します。
function itemSize(index) {
  return index % 2 ? 50 : 25;
}

METHODS

設定できるメソッドは、FixedSizeListで使えるpropsに加えて、下記のメソッドが使えるようになっています。

resetAfterIndex(index: number, shouldForceUpdate: boolean = true): void

VariableSizeListでは、パフォーマンスを目的として、各インデックスのオフセットと測定サイズをキャッシュしています。

このメソッドは、指定したインデックスの以降すべてのアイテムのキャッシュデータをクリアします。 アイテムのサイズが変更されるたびに呼び出されます。

デフォルトでは、インデックスがリセットされた後、リストは自動的に再レンダリングされます。 親コンポーネントで状態の更新が完了したあとに更新したい場合は、shouldForceUpdateにfalseの渡すと強制的に再レンダリングします。

FixedSizeGrid

PROPS

children: component : Required

リストに表示するReactコンポーネントを設定します。

コンポーネントは下記の3つのprop受け取ります。

  • columnIndex: カラム要素番号
  • rowIndex: ロウ要素番号
  • style: コンポーネントに適用するstyle また、FixedSizeGridのuseIsScrolling属性がtrueの場合は、propにisScrollingが追加で渡されます。

ファンクションコンポーネントの場合は下記のようになります。

<FixedSizeGrid {...props}>
  {({ columnIndex, rowIndex, style }) => (
    <div style={style}>
      row {rowIndex}, column {columnIndex}
    </div>
  )}
</FixedSizeGrid>

複雑なアイテムを表示したい場合は、PureComponentを継承して不要な再描画を減らしつつコンポーネントを切り出すことができます。

class ItemRenderer extends PureComponent {
  render() {
    return (
      <div style={this.props.style}>
        row {this.props.rowIndex}, column {this.props.columnIndex}
      </div>
    );
  }
}

// Reference it inside of the render method:

<FixedSizeGrid {...props}>
  {ItemRenderer}
</FixedSizeGrid>
height: number : Required

グリッドの高さを設定します。

これは一度に描画・表示する行数に影響を与えます。

width: number : Required

グリッドの幅を設定します。

これは一度に描画・表示する列すうに影響を与えます。

rowCount: number : Required

行数を設定します。

一度にいくつかの行のみがレンダリングおよび表示されることに注意してください。

columnCount: number : Required

グリッドのカラム数を設定します。

一度にいくつかの列のみがレンダリングおよび表示されることに注意してください。

rowHeight: number : Required

個別のグリッドの高さを指定します。

columnWidth: number : Required

個別のグリッドの幅を指定します。

className: string = ""

一番外側の

要素に任意のCSS適用用のclassを追加します、

direction: string = "ltr"

テキストの方向とスクロールの方向を設定します。

  • ltr (default)
  • rtl このプロパティは、グリッドコンポーネント内のCSS direction style に自動的適用されます。
initialScrollLeft: number = 0

初期描画時の水平スクロールオフセットを設定します。

initialScrollTop: number = 0

初期描画時の垂直スクロールオフセットを設定します。

innerRef: function | createRef object

内部コンテナ要素へのRefを追加するための設定値です。

これはアドバンスプロパティです。

innerElementType: React$ElementType = "div"

内部コンテナ要素をタグ名で指定します。

これはアドバンスプロパティです。デフォルトではdivが使われます。

itemData: any

グリッドのアイテムとしてレンダリングするコンポーネントへ渡すデータを設定します。

この設定項目は、レンダリングするコンポーネントをクラスとして切り出す時に使います。

class ComponentThatRendersAGridOfItems extends PureComponent {
  render() {
    // Pass the data source to the item renderer component as itemData:
    return (
      <FixedSizeGrid
        itemData={this.props.itemsArray}
        {...otherGridProps}
      >
        {ItemRenderer}
      </FixedSizeGrid>
    );
  }
}

レンダラーリングするコンポーネントは下記のような実装になります。

class ItemRenderer extends PureComponent {
  render() {
    const { columnIndex, data, rowIndex, style } = this.props;

    // Access the data source using the "data" prop:
    const item = data[rowIndex][columnIndex];

    return (
      <div style={style}>
        {item}
      </div>
    );
  }
}
itemKey: function

デフォルトでは、グリッドはアイテムをキーとして使用します。 次の場合は問題ありません。

  • アイテムがソートされない。
  • アイテムが変更されない。
  • PureComponentを拡張したクラスを使用しない。
  • アイテムが状態を持っていない。 グリッドが上記の制約を満たさない場合は、itemKeyプロパティを使用して、アイテムに独自のキーを指定します。
function itemKey({ columnIndex, data, rowIndex }) {
  // Find the item for the specified indices.
  // In this case "data" is an Array that was passed to Grid as "itemData".
  const item = data[rowIndex];

  // Return a value that uniquely identifies this item.
  // For a grid, this key must represent both the row and column.
  // Typically this will be something dynamic like a UID for the row,
  // Mixed with something more static like the incoming column index.
  return `${item.id}-${columnIndex}`;
}
onItemsRendered: function

グリッドの変更によってレンダリングされるアイテムが変更された時に呼ばれるコールバックメソッドです。

このコールバックは、アイテムのインデックスが変更されたときにのみ呼び出されます。 アイテムが他の理由(isScrollingやdata paramsの変更など)で再レンダリングされた場合は呼び出されません。

  • overscanColumnStartIndex : オーバースキャン(リスト外で見えてはいない)開始要素列番号
  • overscanColumnStopIndex : オーバースキャン(リスト外で見えてはいない)停止要素列番号
  • overscanRowStartIndex : オーバースキャン(リスト外で見えてはいない)開始要素行番号
  • overscanRowStopIndex : オーバースキャン(リスト外で見えてはいない)停止要素行番号
  • visibleColumnStartIndex : 表示開始要素列番号
  • visibleColumnStopIndex : 表示停止要素列番号
  • visibleRowStartIndex : 表示開始要素行番号
  • visibleRowStopIndex : 表示停止要素行番号
function onItemsRendered({
  overscanColumnStartIndex,
  overscanColumnStopIndex,
  overscanRowStartIndex,
  overscanRowStopIndex,
  visibleColumnStartIndex,
  visibleColumnStopIndex,
  visibleRowStartIndex,
  visibleRowStopIndex
}) {
  // All index params are numbers.
}
onScroll: function

ユーザーのスクロールやscrollToメソッドによりグリッドの位置が変化した時に呼ばれます。

  • horizontalScrollDirection : 水平方向のスクロール方向
  • scrollLeft : 左端からのスクロール位置
  • scrollTop : 上端からのスクロール位置
  • scrollUpdateWasRequested : スクロールの発生原因がメソッド(scrollToやscrollToItem)による場合はtrue、ユーザー操作の場合はfalseが渡されます。
  • verticalScrollDirection : 垂直方向のスクロール方向
function onScroll({
  horizontalScrollDirection,
  scrollLeft,
  scrollTop,
  scrollUpdateWasRequested,
  verticalScrollDirection
}) {
  // horizontalDirection and verticalDirection are either "forward" or "backward".

  // scrollLeft and scrollTop are numbers.

  // scrollUpdateWasRequested is a boolean.
  // This value is true if the scroll was caused by scrollTo() or scrollToItem(),
  // And false if it was the result of a user interaction in the browser.
}
outerRef: function | createRef object

最外部のコンテナ要素にRefを設定します。

outerElementType: React$ElementType = "div"

最外部のコンテナ要素タイプをタグ名で指定します。 大抵の場合はdivが使用されます。

outerTagName: string

このプロパティは非推奨となっています。 最外部のコンテナ要素にタグ名を設定します。

overscanColumnCount: number = 1

現在、グリッドの可視エリアに表示しているアイテムの外側にある、列方向アイテムをレンダリングする数を指定します。

このプロパティは下記の2点からとても重要です。

  • オーバースキャンは、タブキーによるフォーカス移動を可能にします。
  • オーバースキャンは、ユーザーが最初にスクロール開始時の空きスペースのフラッシュが減少、防止します。

大量のオーバースキャンをすると、パフォーマンスに悪影響を与えるので注意が必要です。 デフォルトでは、1つ分のアイテムをオーバースキャンします。

overscanRowCount: number = 1

現在、グリッドの可視エリアに表示しているアイテムの外側にある、行方向アイテムをレンダリングする数を指定します。

このプロパティは下記の2点からとても重要です。

  • オーバースキャンは、タブキーによるフォーカス移動を可能にします。
  • オーバースキャンは、ユーザーが最初にスクロール開始時の空きスペースのフラッシュが減少、防止します。

大量のオーバースキャンをすると、パフォーマンスに悪影響を与えるので注意が必要です。 デフォルトでは、1つ分のアイテムをオーバースキャンします。

overscanCount: number

このプロパティは非推奨です。 overscanColumnCountとoverscanRowCountを代わりに使用してください。

overscanColumnsCount: number

このプロパティは非推奨です。 overscanColumnCountを代わりに使用してください。

overscanRowsCount: number

このプロパティは非推奨です。 overscanRowCountを代わりに使用してください。

style: Object = null

最外部のdiv要素に任意のスタイルをインラインで設定します。

useIsScrolling: boolean = false

レンダラー(関数やコンポーネントクラス)に対して、isScrollingパラメータを追加で渡します。 このパラメータは、スクロール中に行・列にプレースホルダーを表示するために使用します。

このプロパティを使用した場合、スクロールが停止した時に追加で描画処理が呼ばれることに注意指定ください。

METHODS

scrollTo({scrollLeft: number, scrollTop: number}): void

スクロール量を指定して、リストをスクロールさせます。

  • scrollTop : 上端部からのスクロール量を指定します。
  • scrollLeft : 左端部からのスクロール量を指定します。
scrollToItem({align: string = "auto", columnIndex?: number, rowIndex?: number }): void

アイテムの列要素番号・行要素番号を指定してスクロールを行います。

デフォルトでは、指定されたアイテムが表示されるようにリストのスクロール量がなるべく少なくなるようになっています。 この挙動は、第二引数を指定することで変更することができます。

  • auto (default) : アイテムが表示される最小のスクロール量でスクロールを行います。既に表示されている場合はスクロールは発生しません。
  • smart : 既に表示されている場合はスクロールは発生しません。If it is less than one viewport away, scroll as little as possible so that it becomes visible. If it is more than one viewport away, scroll so that it is centered within the list.
  • center : アイテムがグリッドの中央に表示されるようにスクロールをします。
  • end : アイテムがグリッドの右下に表示されるようにスクロールをします。
  • start : アイテムがグリッドの左上に表示されるようにスクロールをします。

VariableSizeGrid

FixedSizeGridと同様のプロパティに加えて、下記のプロパティが追加で設定できます。

columnWidth: (index: number) => number

任意のカラムの幅を返すメソッドを設定します。

function columnWidth(index) {
  return index % 2 ? 50 : 25;
}

rowHeight: (index: number) => number

任意の行の高さを返すメソッドを設定します。

function rowHeight(index) {
  return index % 2 ? 50 : 25;
}

estimatedColumnWidth: number = 50

描画されていないカラムの平均または大体の列の幅を設定します。

この値は全ての幅の幅が計測される前にグリッドの全幅を見積もるために使用されます。

この見積もった幅はユーザーのスクロール行為に影響を与えます。

列が計測されると更新されます。

estimatedRowHeight: number = 50

描画されていないカラムの平均または大体の行の高さを設定します。

この値は全てのカラムの高さが計測される前にグリッドの全高さを見積もるために使用されます。

この見積もった高さはユーザーのスクロール行為に影響を与えます。

行が計測されると更新されます。

METHODS

This component has the same methods as FixedSizeGrid, but with the following additions:

resetAfterIndices({ columnIndex: number, rowIndex: number, shouldForceUpdate: boolean = true }): void

VariableSizeGridでは、パフォーマンスを目的として、各インデックスのオフセットと測定サイズをキャッシュしています。

このメソッドは、指定したインデックスの以降すべてのアイテムのキャッシュデータをクリアします。 アイテムのサイズが変更されるたびに呼び出されます。

デフォルトでは、インデックスがリセットされた後、グリッドは自動的に再レンダリングされます。 親コンポーネントで状態の更新が完了したあとに更新したい場合は、shouldForceUpdateにfalseの渡すと強制的に再レンダリングします。

resetAfterColumnIndex(index: number, shouldForceUpdate: boolean = true): void

VariableSizeGridでは、パフォーマンスを目的として、各インデックスのオフセットと測定サイズをキャッシュしています。

このメソッドは、指定したインデックスの以降すべての列のキャッシュデータをクリアします。 アイテムのサイズが変更されるたびに呼び出されます。

デフォルトでは、インデックスがリセットされた後、グリッドは自動的に再レンダリングされます。 親コンポーネントで状態の更新が完了したあとに更新したい場合は、shouldForceUpdateにfalseの渡すと強制的に再レンダリングします。

resetAfterRowIndex(index: number, shouldForceUpdate: boolean = true): void

It should be called whenever a row's height changes. (Note that this is not a typical occurrance.)

By default the grid will automatically re-render after the index is reset. If you would like to delay this re-render until e.g. a state update has completed in the parent component, specify a value offalse for the second, optional parameter.

VariableSizeGridでは、パフォーマンスを目的として、各インデックスのオフセットと測定サイズをキャッシュしています。

このメソッドは、指定したインデックスの以降すべての行のキャッシュデータをクリアします。 行の高さが変更されるたびに呼び出されます。

デフォルトでは、インデックスがリセットされた後、グリッドは自動的に再レンダリングされます。 親コンポーネントで状態の更新が完了したあとに更新したい場合は、shouldForceUpdateにfalseの渡すと強制的に再レンダリングします。

METHODS

areEqual

React.memo用のカスタム比較関数 アイテムのレンダーにクラスコンポーネントを使っている場合は、shouldComponentUpdateを代わりに使用してください。

import React, { memo } from "react";

// Step 1: Import areEqual from react-window
import { areEqual } from "react-window";

const Row = memo(
  props => {
    const { index, style } = props;

    return <div style={style}>Row {index}</div>;
  },

  // Step 2: Pass it as the second argument to React.memo()
  areEqual
);

shouldComponentUpdate

クラスコンポーネントのカスタムshouldComponentUpdate実装。 アイテムレンダラーが機能コンポーネントの場合は、代わりにareEqual比較メソッドを使用してください。

import React, { Component } from "react";

// Step 1: Import shouldComponentUpdate from react-window
import { shouldComponentUpdate } from "react-window";

class Row extends Component {
  // Step 2: Bind the sCU method to the component instance
  shouldComponentUpdate = shouldComponentUpdate.bind(this);

  render() {
    const { index, style } = this.props;

    return <div style={style}>Row {index}</div>;
  }
}