Search code examples
javascriptreactjsreact-virtualized

How to add sorting to table with react virtualized?


I am trying to add sorting to my project with the table sorting demo on Github.

My code:

import React from 'react';
import PropTypes from 'prop-types';
import { Table, Column, SortDirection, SortIndicator } from 'react-virtualized';
import AutoSizer from 'react-virtualized/dist/commonjs/AutoSizer';

import 'react-virtualized/styles.css';

class NewTable extends React.Component {
  constructor(props) {
    super(props);

    this.dataList = props.list;

    this.state = {
      headerHeight: 50,
      rowHeight: 25,
      rowCount: this.dataList.length,
      height: 400,
      sortBy: 'columnone',
      sortDirection: SortDirection.ASC,
    };

    this.headerRenderer = this.headerRenderer.bind(this);
    this.sort = this.sort.bind(this);
  }

  isSortEnabled() {
    const list = this.dataList;
    const rowCount = this.state;

    return rowCount <= list.length;
  }

  sort({ sortBy, sortDirection }) {
    this.setState({ sortBy, sortDirection });
  }

  headerRenderer({
    dataKey,
    sortBy,
    sortDirection,
  }) {
    return (
      <div>
        Column One
        {sortBy === dataKey &&
          <SortIndicator sortDirection={sortDirection} />
        }
      </div>
    );
  }

  render() {
    const {
      headerHeight,
      height,
      rowHeight,
      sortBy,
      sortDirection,
    } = this.state;

    const list = this.dataList;
    const sortedList = this.isSortEnabled() ?
      (list.sortBy(item => item[sortBy]).update(list => sortDirection === SortDirection.DESC ?
        list.reverse() : list))
      : list;

    return (
      <AutoSizer disableHeight>
        {({ width }) => (
          <Table
            headerHeight={headerHeight}
            height={height}
            rowCount={list.length}
            rowGetter={({ index }) => sortedList[index]}
            rowHeight={rowHeight}
            sort={this.sort}
            sortBy={sortBy}
            sortDirection={sortDirection}
            width={width}
          >
            <Column
              dataKey='columnone'
              headerRenderer={this.headerRenderer}
              disableSort={!this.isSortEnabled}
              width={200}
              flexGrow={1}
            />
          </Table>
        )}
      </AutoSizer>
    );
  }
}

export default NewTable;

My code shows the ASC and DESC arrows flipping up and down when clicked, but the actual sorting does not happen. What am I missing?

I don't really understand where the sorting is happening. I see the functions, but I don't see where the output goes.

Thank you!

EDIT: Data enters as JSON.


Solution

  • The code snippet you pasted, like the react-virtualized Table demo page, do the sorting inline, within the render function:

    const sortedList = this._isSortEnabled()
      ? list
          .sortBy(item => item[sortBy])
          .update(
            list =>
              sortDirection === SortDirection.DESC ? list.reverse() : list
          )
      : list;
    

    This probably isn't what you want for a production app, since it would have to re-sort the data each time a component rendered. Instead you'd probably want to sort the data only once- when the sortBy field or sortDirection change- and then store a sorted version of the data in your component state (or in Redux if you use it).

    Table tells you when the data needs to be sorted/resorted by calling the sort prop you provide. In your example above, that means this function is called:

    sort({ sortBy, sortDirection }) {
      this.setState({ sortBy, sortDirection });
    }
    

    Since you're storing the sort criteria in your component state, you can also store the sorted result in state also:

    sort({ sortBy, sortDirection }) {
      const sortedList = list
        .sortBy(item => item[sortBy])
        .update(
          list =>
            sortDirection === SortDirection.DESC ? list.reverse() : list
        );
    
      this.setState({ sortBy, sortDirection, sortedList });
    }
    

    The only thing that's left is for your rowGetter function to use the sortedList from state rather to retrieve rows.