React-virtualized: Create new reusable id-based cache for CellMeasurer

Created on 5 Dec 2016  路  6Comments  路  Source: bvaughn/react-virtualized

The default cache for CellMeasurer currently stores sizes using indices. It should be possible to store these values using (data) ids instead so that changes to the underlying data (eg sorting, new rows appended) wouldn't invalidate the cached sizes. This would be useful for things like reverse-ordered lists (eg chat).

cc @zsherman

enhancement

Most helpful comment

I had some performance issue with the CellMeasurer for highly dynamic row height. So i've come up with a simple solution(key based caching) that seemed to resolve also this issue.

I wanted to use the CellMeasurer to calculate the dynamic height but only when a similar row hasn't been computed yet. For example let's say that each row has 0..n child I could use the child count as my key and still benefits from the CellMeasurer to measure all my item + padding/margin.

If someone is interested here is my implementation:

export default class KeyBasedCellSizeCache {
  constructor ({
      buildColumnKey = (index) => (0),
      buildRowKey = (index) => (0)
    } = {}) {
    this._buildColumnKey = buildColumnKey;
    this._buildRowKey = buildRowKey;

    this._keyByRowIndex = {}
    this._keyByColumnIndex = {}

    this._heightByRowKey = {}
    this._widthByColKey = {}
  }

  clearAllColumnWidths () {
    this._keyByRowIndex = {}
  }

  clearAllRowHeights () {
    this._keyByRowIndex = {}
  }

  clearColumnWidth (index) {
    delete this._keyByColumnIndex[index]
  }

  clearRowHeight (index) {
    delete this._keyByRowIndex[index]
  }

  getColumnWidth (index) {
    return this._widthByColKey[this.getColumnKey(index)]
  }

  getRowHeight (index) {
    return this._heightByRowKey[this.getRowKey(index)]
  }

  setColumnWidth (index, width) {
    this._widthByColKey[this.getColumnKey(index)] = width
  }

  setRowHeight (index, height) {
    this._heightByRowKey[this.getRowKey(index)] = height
  }

  getRowKey(index) {
    if(!this._keyByRowIndex[index]) {
      this._keyByRowIndex[index] = this._buildRowKey(index)
    }

    return this._keyByRowIndex[index]
  }

  getColumnKey(index) {
    if(!this._keyByColumnIndex[index]) {
      this._keyByColumnIndex[index] = this._buildColumnKey(index)
    }

    return this._keyByColumnIndex[index]
  }
}

And thanks for this awesome library!

All 6 comments

+1

Ran into the need for this myself a few weeks back (as described in a side-note on #443). This can be a source of perf issues on low powered devices when sorting large lists of complex items (since they need to be re-measured every time).

Yep, we're using this strategy right now without CellMeasurer, just using our own cache and passing heights straight to List. Having this baked in would be awesome though...

If you're able, share your impl here @zsherman. I can use it as a starting point (or perhaps use it as-is).

馃憤
@bvaughn I can help you with it too. In my application I already create this kind cache.

Maybe the time is come - we need to create epic issue for official and full featured react-virtualized chat example (with rows appended to top, scroll position saving and so on). There are a lot of chat developers I think.

The more the merrier, yeah.

Tempted to say we should hold off on the chat application example until I've had a chance to finish tackling #396 as I think it will address performance problems that come with reverse display and hopefully simplify the process a bit. But I don't know when I'll finish that, so...yeah, maybe we should do it now and just update the example later. 馃榿 I'd welcome such an example to the site/docs.

I had some performance issue with the CellMeasurer for highly dynamic row height. So i've come up with a simple solution(key based caching) that seemed to resolve also this issue.

I wanted to use the CellMeasurer to calculate the dynamic height but only when a similar row hasn't been computed yet. For example let's say that each row has 0..n child I could use the child count as my key and still benefits from the CellMeasurer to measure all my item + padding/margin.

If someone is interested here is my implementation:

export default class KeyBasedCellSizeCache {
  constructor ({
      buildColumnKey = (index) => (0),
      buildRowKey = (index) => (0)
    } = {}) {
    this._buildColumnKey = buildColumnKey;
    this._buildRowKey = buildRowKey;

    this._keyByRowIndex = {}
    this._keyByColumnIndex = {}

    this._heightByRowKey = {}
    this._widthByColKey = {}
  }

  clearAllColumnWidths () {
    this._keyByRowIndex = {}
  }

  clearAllRowHeights () {
    this._keyByRowIndex = {}
  }

  clearColumnWidth (index) {
    delete this._keyByColumnIndex[index]
  }

  clearRowHeight (index) {
    delete this._keyByRowIndex[index]
  }

  getColumnWidth (index) {
    return this._widthByColKey[this.getColumnKey(index)]
  }

  getRowHeight (index) {
    return this._heightByRowKey[this.getRowKey(index)]
  }

  setColumnWidth (index, width) {
    this._widthByColKey[this.getColumnKey(index)] = width
  }

  setRowHeight (index, height) {
    this._heightByRowKey[this.getRowKey(index)] = height
  }

  getRowKey(index) {
    if(!this._keyByRowIndex[index]) {
      this._keyByRowIndex[index] = this._buildRowKey(index)
    }

    return this._keyByRowIndex[index]
  }

  getColumnKey(index) {
    if(!this._keyByColumnIndex[index]) {
      this._keyByColumnIndex[index] = this._buildColumnKey(index)
    }

    return this._keyByColumnIndex[index]
  }
}

And thanks for this awesome library!

Was this page helpful?
0 / 5 - 0 ratings

Related issues

rodcorsi picture rodcorsi  路  3Comments

bee0060 picture bee0060  路  3Comments

clauderic picture clauderic  路  3Comments

pkumar84 picture pkumar84  路  4Comments

SBoudrias picture SBoudrias  路  3Comments