React-virtualized: Cell hover effects

Created on 29 Mar 2016  Â·  8Comments  Â·  Source: bvaughn/react-virtualized

I'm using Grid component and now I'm trying to implement hover effect for columns in Grid. Every time I hover some cell, I'll call custom method and set the state:

this.setState({
    highlightedColumn: columnIndex,
});

This is working. But depends on this state in highlightedColumn I want to set custom class to rendered cells. I'm using _renderCell method:

_renderCell({ columnIndex, rowIndex }) {
        var cellClasses = classNames({
            'GridCell': true,
            'GridCell--odd-row': rowIndex % 2 === 0,
            'GridCell--highlighted': this.state.highlightedColumn === columnIndex,
        });

        return (
            <div className={cellClasses}>
                ... CELL CONTENT ...
            </div>
        );
    },

Problem is that class doesn't update. RenderCell is called only when I scroll with the Grid. Is there any chance to re-render Grid also when I update state of component?

question

Most helpful comment

Works for me? (live demo, source code)

untitled screencast

All 8 comments

Tried using the React forceUpdate method? I think the docs mention this somewhere but... I don't recall. :)

I'm going to close this issue since it's just a question and _I think_ I've actually answered it. But if not, we can keep chatting on the issue. I don't mind. I just don't like leaving non-actionable issues open.

forceUpdate doesn't work in this case. This is our example for big datatable with many columns and fixed header:

    _renderTableCell({ columnIndex, rowIndex }) {
        var cellClasses = classNames({
            'GridCell': true,
            'GridCell--odd-row': rowIndex % 2 === 1,
            'GridCell--highlighted': this.state.highlightedColumn === columnIndex,
        });

        return (
            <div className={cellClasses}>
                SOME CONTENT HERE
            </div>
        );
    },

...

<ScrollSync>
    {({ clientHeight, clientWidth, onScroll, scrollHeight, scrollLeft, scrollTop, scrollWidth }) => {
        return (
            <div>
                <div style={{
                    position: 'relative',
                    height: headerHeight,
                    width: tableWidth - scrollbarSize(),
                }}>
                    <Grid
                        className={'HeaderGrid'}
                        columnWidth={this._getColumnWidth}
                        columnsCount={5}
                        height={headerHeight}
                        onScroll={onScroll}
                        renderCell={this._renderHeaderCell}
                        rowHeight={headerHeight}
                        rowsCount={1}
                        scrollLeft={scrollLeft}
                        width={tableWidth - scrollbarSize()}
                    />
                </div>

                <div
                    style={{
                        height: tableHeight - headerHeight,
                        width: tableWidth,
                    }}
                >
                    <Grid
                        width={tableWidth}
                        height={tableHeight - headerHeight}
                        columnWidth={this._getColumnWidth}
                        rowHeight={this._getRowHeight}
                        columnsCount={5}
                        onScroll={onScroll}
                        rowsCount={pagesCount}
                        renderCell={this._renderTableCell}
                        scrollLeft={scrollLeft}
                    />
                </div>
            </div>
        );
    }}
</ScrollSync>

When called forceUpdate I checked that <Grid> itself is re-rendered, but I expected also calling of this._renderTableCell again. This didn't happen. I set classes for each cell in this._renderTableCell. Is there any different way how to re-render cells? Cell is re-rendered only when I scroll with Grid content.

Thanks

Works for me? (live demo, source code)

untitled screencast

Thanks. Your example help us to solve it. Problem was that I was trying to target Grid directly by this.refs.Grid. This syntax this.refs.AutoSizer.refs.Grid is right in this case.

Ah, glad to hear it :)

On Thursday, March 31, 2016, Martin PeÅ¡out [email protected] wrote:

Thanks. Your example help us to solve it. Problem was that I was trying to
target Grid directly by this.refs.Grid. This syntax
this.refs.AutoSizer.refs.Grid is right in this case.

—
You are receiving this because you modified the open/close state.
Reply to this email directly or view it on GitHub
https://github.com/bvaughn/react-virtualized/issues/167#issuecomment-203808217

Hello,
I have the same problem. Cell is re-rendered only when I scroll with Grid content.
Can you help me please?

class StatisticsGrid extends MultiGrid { _getLeftGridWidth(props) { return 300; } }

export default class Table extends React.Component {

constructor(props) {
    super(props);
    this.state = {
        loading: true,
        hoveredColumnIndex: "",
        hoveredRowIndex: "",

    };

    this._cellRenderer = this._cellRenderer.bind(this);


}`


            <div className="multiGridWrapp">

                <StatisticsGrid
                    {...this.props.appState.data}
                    ref="Grid"
                    cellRenderer={this._cellRenderer}
                    columnWidth={40}
                    columnCount={this.props.appState.data.period.length}
                    enableFixedColumnScroll
                    enableFixedRowScroll
                    fixedColumnCount={1}
                    fixedRowCount={1}
                    height={500}
                    rowHeight={60}
                    rowCount={this.props.appState.data.values.length}
                    style={STYLE}
                    styleBottomLeftGrid={STYLE_BOTTOM_LEFT_GRID}
                    styleTopLeftGrid={STYLE_TOP_LEFT_GRID}
                    styleTopRightGrid={STYLE_TOP_RIGHT_GRID}
                    width={1198}
                    hideTopRightGridScrollbar
                    hideBottomLeftGridScrollba
                />
            </div>
        </React.Fragment>);

_cellRenderer({columnIndex, key, rowIndex, style}) {
let setState = this.setState.bind(this);
let className =
columnIndex === this.state.hoveredColumnIndex ||
rowIndex === this.state.hoveredRowIndex
? 'hoveredCell'
: '';

    let grid = this.refs.Grid;

    // {columnIndex}  {rowIndex}
    let customClass = "cellFirst";
    if (columnIndex != 0 && rowIndex == 0)
        customClass = "cellHeader";
    else if (columnIndex == 0 && rowIndex != 0)
        customClass = "cellRules";
    else if (columnIndex != 0 && rowIndex != 0)
        customClass = "cell";

      customClass += rowIndex%2===1 ? " odd":"";
    return (
        <div onClick={() => { alert("as "); }} onMouseOver={() => { setState({ hoveredColumnIndex: columnIndex, hoveredRowIndex: rowIndex, }); grid.forceUpdate();}} className={customClass + " "+className} key={key} style={style}>
            {columnIndex != 0 && rowIndex == 0 && this.props.appState.data.period[columnIndex].split('-').map(function (data, index) {
                return (<span key={index}>{data}</span>);
            })}
            {columnIndex == 0 && rowIndex != 0 && this.props.appState.data.values.length>0 ? <span>{"ID: "}<b>{this.props.appState.data.values[rowIndex - 1].id} </b>{" - "}{this.props.appState.data.values[rowIndex - 1].name}<span>{this.props.appState.data.values[rowIndex - 1].subject}</span></span>: null}
            {columnIndex != 0 && rowIndex != 0 &&  this.props.appState.data.values.length>0 && this.props.appState.data.values[rowIndex - 1].data[this.props.appState.data.period[columnIndex]]}
        </div>
    );
}

}`

I tried to add let grid = this.refs.Grid; what i saw in your example but only what is changing are the cells in header.
Thanks for your help!
@bvaughn

Done, I solved the problem.
this.refs.Grid shloud be grid.forceUpdateGrids() it's work now fine..

Was this page helpful?
0 / 5 - 0 ratings