0

I'm a little confused if I can use React Virtualized's Collection component to solve my problem. I'll try to describe what I'm doing:

I'm using React Virtualized on a page to display two lists/collections of items. I've finished the first collection which has items that have the same width and height:

Collection where cells have same height & width

The first collection was pretty straight forward and easy to implement. Now I'm working on the second collection which contains images of varying sizes. I want the cells to have the same height but different widths (depending on the image dimensions of course). The problem is that rows might not always have the same number of cells:

Collection where cells have same height but different width

Is this possible to achieve with React Virtualized? If so, how can I determine the position in "cellSizeAndPositionGetter"?

Jooooooooohn
  • 137
  • 1
  • 3
  • 12
  • Short answer: you can't. A workaround I can think of is splitting all the cells in actual rows, so row 1 would have the first three images and row 2 would have the next four in your case. Then you'd virtualize the rows themselves, not the individual cells. It's a "by design" limitation of virtualized lists in general ("list", not "grid"). – Sergiu Paraschiv Jan 12 '17 at 11:13
  • I agree with what Sergiu said. To add a bit more- the top collection you showed really should be a `Grid` rather than a `Component`. `Grid` is more efficient and simpler to use. The second one you show could be a `List` but calculating the number of rows would be a bit tricky. – bvaughn Jan 12 '17 at 16:54
  • @brianvaughn I'm guessing you mean `Collection`and not `Component` ? Anyways, I see what you both are saying. I think I will try the `Grid` for the first collection. For the second collection I ended up using [CassetteRocks/react-infinite-scroller](https://github.com/CassetteRocks/react-infinite-scroller). When I have some spare time I will try implementing the second collection using React Virtualized again. Because I really like the repo! :) Thank you both for your input. – Jooooooooohn Jan 13 '17 at 07:08
  • Sorry, yes. Mis-typed. :) Glad to hear! – bvaughn Jan 13 '17 at 16:42

1 Answers1

2

I recently used react-virtualized List to display rows of fixed-height, variable-width image cards and it worked great.

My List rowRenderer uses an array of rows of image card elements. That is, an array of arrays of react components, as JSX.

See my final function, cardsRows, for how I build the rows based on element widths and screen width.

Here's how it looks:

List rows layout

Hope this helps!

Some snippets of my code:

import {AutoSizer, List} from 'react-virtualized';

...

updateDimensions() {
    this.setState({
        screenWidth: window.innerWidth,
    });
}

componentDidMount() {
    window.addEventListener("resize", this.updateDimensions);
}


componentDidUpdate(prevProps, prevState) {
    const props = this.props;
    const state = this.state;

    if (JSON.stringify(props.imageDocs) !== JSON.stringify(prevProps.imageDocs) || state.screenWidth !== prevState.screenWidth)
        this.setState({
            cardsRows: cardsRows(props, state.screenWidth),
        });
}


rowRenderer({key, index, style, isScrolling}) {
    if (!this.state.cardsRows.length)
        return '';
    return (
        <div id={index} title={this.state.cardsRows[index].length} key={key} style={style}>
            {this.state.cardsRows[index]}
        </div>
    );
}

...

render() {
    return (
            <div style={styles.subMain}>
                <AutoSizer>
                    {({height, width}) => (<List height={height}
                                                 rowCount={this.state.cardsRows.length}
                                                 rowHeight={164}
                                                 rowRenderer={this.rowRenderer}
                                                 width={width}
                                                 overscanRowCount={2}
                        />
                    )}
                </AutoSizer>
            </div>
    );
}

...

const cardsRows = (props, screenWidth) => {

    const rows = [];
    let rowCards = [];
    let rowWidth = 0;
    const distanceBetweenCards = 15;

    for (const imageDoc of props.imageDocs) {
        const imageWidth = getWidth(imageDoc);

        if (rowWidth + distanceBetweenCards * 2 + imageWidth <= screenWidth) {
            rowCards.push(cardElement(imageDoc));
            rowWidth += distanceBetweenCards + imageWidth;
        }
        else {
            rows.push(rowCards);
            rowCards = [];
            rowWidth = distanceBetweenCards;
        }
    }
    if (rowCards.length) {
        rows.push(rowCards);
    }
    return rows;
};


const styles = {
    subMain: {
        position: 'absolute',
        display: 'block',
        top: 0,
        right: 0,
        left: 0,
        bottom: 0,
    }
};
stuart
  • 1,785
  • 2
  • 26
  • 38