<!DOCTYPE html>
<html>
  <head>
    <script crossorigin src="https://unpkg.com/react@16/umd/react.development.js"></script>
    <script crossorigin src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>
    <script crossorigin src="https://unpkg.com/react-virtualized/dist/umd/react-virtualized.js"></script>

    <link rel="stylesheet" href="https://unpkg.com/react-virtualized/styles.css">
    <link rel="stylesheet" href="styles.css">
  </head>

  <body>
    <div id="example">
      <!-- This element's contents will be replaced with your code... -->
    </div>
    <script src="script.js"></script>
  </body>

</html>
const PureComponent = React.PureComponent;
const createCellPositioner = ReactVirtualized.createMasonryCellPositioner;
const CellMeasurerCache = ReactVirtualized.CellMeasurerCache;
const AutoSizer = ReactVirtualized.AutoSizer;

const BADGE_COLORS = [
  '#f44336',
  '#3f51b5',
  '#4caf50',
  '#ff9800',
  '#2196f3',
  '#374046',
  '#cddc39',
  '#2196f3',
  '#9c27b0',
  '#ffc107',
  '#009688',
  '#673ab7',
  '#ffeb3b',
  '#cddc39',
  '#795548',
];
const ROW_HEIGHTS = [50, 75, 100];

const loremIpsum = [
  'Curabitur condimentum tellus sit amet neque posuere, condimentum tempus purus eleifend, ac iaculis erat nunc in dolor.',
  'Donec tempus, augue id hendrerit pretium, mauris leo congue nulla, ac iaculis erat nunc in dolor.',
];

function generateRandomList(count) {
  const list = [];

  for (var i = 0; i < count; i++) {
    const random = loremIpsum[i % loremIpsum.length];
    const randoms = [random];

    for (let j = Math.round(Math.random() * 10); j--; ) {
      randoms.push(loremIpsum[(i * j) % loremIpsum.length]);
    }

    list.push({
      color: BADGE_COLORS[i % BADGE_COLORS.length],
      index: i,
      random,
      randomLong: randoms.join(' '),
      size: ROW_HEIGHTS[Math.floor(Math.random() * ROW_HEIGHTS.length)],
    });
  }

  return list;
}

// List data as an array of strings
const list = generateRandomList(20);

class Masonry extends ReactVirtualized.Masonry {
  _getEstimatedTotalHeight() {
    const {cellCount, cellMeasurerCache, width} = this.props;

    const estimatedColumnCount = Math.floor(
      width / cellMeasurerCache.defaultWidth,
    );
    
    const estimateTotalHeight = this._positionCache.estimateTotalHeight(
      cellCount,
      estimatedColumnCount,
      cellMeasurerCache.defaultHeight,
    );
    
    return isFinite(estimateTotalHeight) ? estimateTotalHeight : 0;
  }
}

class CellMeasurer extends ReactVirtualized.CellMeasurer {
  componentDidUpdate() {
    this._maybeMeasureCell();
    this._measure();
  }
}

class MasonryExample extends PureComponent {
  constructor(props, context) {
    super(props, context);

    this._gutterSize = 10;
    this._columnCount = 0;
    this._columnWidth = 200;

    this._cache = new CellMeasurerCache({
      defaultHeight: 250,
      defaultWidth: 200,
      fixedWidth: true,
      fixedHeight: false,
    });

    this.state = {
      list: generateRandomList(20),
      height: 300,
      overscanByPixels: 0,
      windowScrollerEnabled: false,
    };

    this._onResize = this._onResize.bind(this);
    this._reset = this._reset.bind(this);
    this._setMasonryRef = this._setMasonryRef.bind(this);
    this._cellRenderer = this._cellRenderer.bind(this);
  }
  
  componentDidMount() {
    setTimeout(() => {
      this.setState(state => ({
        list: state.list.concat(generateRandomList(20))
      }), () => {
        console.log(this.state);
        // this.reset();
      });
    }, 2000)
  }
  
  _onResize({width}) {
    console.log('_onResize');
    this._width = width;

    this._calculateColumnCount();
    this._resetCellPositioner();
    this._masonry.recomputeCellPositions();
    // this._reset();
  }
  
  _reset() {
    this._calculateColumnCount();

    this._cache.clearAll();
    this._resetCellPositioner();
    this._masonry.clearCellPositions();
  };
  
  _resetCellPositioner() {
    this._cellPositioner.reset({
      columnCount: this._columnCount,
      columnWidth: this._columnWidth,
      spacer: this._gutterSize,
    });
  }
  
  _setMasonryRef(ref) {
    this._masonry = ref;
  }
  
  _cellRenderer({index, key, parent, style}) {
    const { list } = this.state;
    const datum = list[index % list.length];
    style.width = this._columnWidth;
    return (
      <CellMeasurer cache={this._cache} index={index} key={key} parent={parent}>
        <div
          className="ReactVirtualized__Masonry-item"
          style={style}
        >
          <div
            style={{
              backgroundColor: datum.color,
              borderRadius: '0.5rem',
              height: datum.size * 3,
              marginBottom: '0.5rem',
              width: '100%',
              fontSize: 20,
              color: 'white',
              display: 'flex',
              alignItems: 'center',
              justifyContent: 'center',
            }}>
            {index}
          </div>
          {datum.random}
        </div>
      </CellMeasurer>
    );
  }
  
  _calculateColumnCount() {
    const brakePoints = [640, 768, 920, 1200, 1400];
    this._columnCount = brakePoints.reduceRight( (p, c, i) => {
      return c < this._width ? p : i;
    }, brakePoints.length) + 1;

    this._columnWidth = Math.floor(
      (this._width - this._columnCount * this._gutterSize) / this._columnCount
    );
  }
  
  _initCellPositioner() {
    if (typeof this._cellPositioner === 'undefined') {
      this._cellPositioner = createCellPositioner({
        cellMeasurerCache: this._cache,
        columnCount: this._columnCount,
        columnWidth: this._columnWidth,
        spacer: this._gutterSize,
      });
    }
  }
  
  render() {
    const { list } = this.state;

    return (
      <AutoSizer
        onResize={this._onResize}
      >
        {({width, height}) => {
          this._width = width;

          this._calculateColumnCount();
          this._initCellPositioner();

          const {overscanByPixels} = this.state;
          return (
            <Masonry
              autoHeight={false}
              cellCount={list.length}
              cellMeasurerCache={this._cache}
              cellPositioner={this._cellPositioner}
              cellRenderer={this._cellRenderer}
              height={height}
              overscanByPixels={overscanByPixels}
              ref={this._setMasonryRef}
              width={width}
              style={{
                willChange: 'auto',
              }}
            />
          );
        }}
      </AutoSizer>
    );
  }
  
}

console.log(MasonryExample);

ReactDOM.render(
  <MasonryExample />,
  document.getElementById('example')
);
html, body, #example {
  width: 100%;
  height: 100%;
  margin: 0;
  padding: 0;
}

#example > div {
  width: 100%  !important;
  height: 100% !important;
}

.ReactVirtualized__Masonry {
  width: 100%  !important;
  height: 100%;
}

.ReactVirtualized__Masonry__innerScrollContainer {
  width: 100% !important;
  height: 100%;
}

.ReactVirtualized__Masonry-item {
  box-sizing: border-box;
  border-radius: .5rem;
  padding: .5rem;
  background-color: #f7f7f7;
  border: 1px solid #ccc;
  word-break: break-all;
  font-size: 1.6rem;
}