1

I have tried now for a couple of hours to add sort functionality on a react-virtualized table, but can't get my head around it... here is the example and source code. Sorry, I was not able to create a plnkr, so I have attached a link to my github repo.

I get this error, when I submit the form with a input text SCREW for example:

TypeError: list.sortBy is not a function
    at PartTable._sortList (PartTable.js:136)
    at new PartTable (PartTable.js:18)

In the example on the site they uses context (const { list } = this.context;) instead of props. Maybe this is the issue?

When I console log this.props.list I get the correct list (see the two example documents)

_sortList({ sortBy, sortDirection }) {
    const { list } = this.props;

    // console.log(list);

    return list
      .sortBy(item => item[sortBy])
      .update(
        list => (sortDirection === SortDirection.DESC ? list.reverse() : list)
      );
  }

Here are two objects coming from my server and into props.list

[
  {
    customsTariff: "73181568",
    facility: "SDC",
    netWeight: "0,07",
    partName: "CAPSCREW",
    partNumber: "3121210233",
    __v: 0,
    _id: "59a9429ac0b7467bf084eb6e"
  },
  {
    customsTariff: "73481568",
    facility: "SDC",
    netWeight: "0,08",
    partName: "CAPSCREW2",
    partNumber: "3121210333",
    __v: 0,
    _id: "59a9429ac0b7463bf084eb6e"
  }
];

Here is the code from PartTable.js

import React, { PureComponent } from "react";
import { AutoSizer, Column, Table } from "react-virtualized";
import { CSVLink, CSVDownload } from "react-csv";
import Button from "material-ui/Button";
import PropTypes from "prop-types";
import SortDirection from "./SortDirection";
import SortIndicator from "./SortIndicator";
import Checkbox from "material-ui/Checkbox";
import "react-virtualized/styles.css";
import "../../styles/App.css";
import styles from "./Table.example.css";

export default class PartTable extends PureComponent {
  constructor(props) {
    super(props);
    const sortBy = "partNumber"; // I want to sort by partNumber by default
    const sortDirection = SortDirection.ASC;
    const sortedList = this._sortList({ sortBy, sortDirection });

    this.state = {
      sortBy,
      sortDirection,
      rowCount: 1000,
      sortedList
    };
    this._noRowsRenderer = this._noRowsRenderer.bind(this);
    this._generateCheckbox = this._generateCheckbox.bind(this);
    this._sort = this._sort.bind(this);
  }

  render() {
    // console.log(this.props.list);
    const { sortBy, sortDirection, sortedList } = this.state;
    const rowGetter = ({ index }) => this._getDatum(sortedList, index);
    const { list, headers } = this.props;
    return (
      <div>
        <AutoSizer disableHeight>
          {({ width }) => (
            <Table
              width={width}
              height={500}
              headerHeight={50}
              rowHeight={50}
              rowCount={list.length}
              rowGetter={rowGetter}
              noRowsRenderer={this._noRowsRenderer}
              sort={this._sort}
              sortBy={sortBy}
              sortDirection={sortDirection}
            >
              {headers.map(header => {
                return (
                  <Column
                    key={header.id}
                    label={header.label}
                    dataKey={header.id}
                    disableSort
                    width={100}
                    flexGrow={1}
                    cellRenderer={
                      header.index ? this._generateCheckbox : undefined
                    }
                  />
                );
              })}
            </Table>
          )}
        </AutoSizer>
        <CSVLink data={list}>
          <Button color="accent">Export {list.length} records to CSV</Button>
        </CSVLink>
      </div>
    );
  }

  _noRowsRenderer() {
    return <div>No Parts here...</div>;
  }
  _generateCheckbox(event) {
    // console.log(event);
    return (
      <div className="table-check-box">
        {this.props.activeCheckboxes && (
          <Checkbox
            onChange={() => this.props._activeCheckbox(event.rowData._id)}
            checked={this.props.activeCheckboxes.includes(event.rowData._id)}
          />
        )}
        {event.cellData}
      </div>
    );
  }
  _isSortEnabled() {
    const { list } = this.props;
    const { rowCount } = this.state;
    return rowCount <= list.size;
  }

  _getDatum(list, index) {
    return list.get(index % list.size);
  }

  _sort({ sortBy, sortDirection }) {
    const sortedList = this._sortList({ sortBy, sortDirection });

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

  _sortList({ sortBy, sortDirection }) {
    const { list } = this.props;

    // console.log(list);

    return list
      .sortBy(item => item[sortBy])
      .update(
        list => (sortDirection === SortDirection.DESC ? list.reverse() : list)
      );
  }
  _rowClassName({ index }) {
    if (index < 0) {
      return styles.headerRow;
    } else {
      return index % 2 === 0 ? styles.evenRow : styles.oddRow;
    }
  }
}

PartTable.PropTypes = {
  list: PropTypes.arrayOf({}).isRequired,
  activeCheckboxes: PropTypes.arrayOf({}),
  _activeCheckbox: PropTypes.func,
  headers: PropTypes.arrayOf({}.isRequired)
};

1 Answer 1

2

Based on the fact that your code has references to list.length- it seems like the list prop you're accepting might be an Array? The sortBy method (assuming you lifted this from the react-virtualized docs) belongs to an Immutable JS List. To sort a JavaScript Array you'll want to use Array.prototype.sort.

PS. The code you pasted has a couple of other references to List methods (eg list.get(index)) that you'll want to replace with brackets (eg list[index]) if I'm correct that you're using an Array.

Full code solution for PartTable.js (Provided by author)

import React, { PureComponent } from "react";
import {
  AutoSizer,
  Column,
  Table,
  SortDirection,
  SortIndicator
} from "react-virtualized";
import { CSVLink, CSVDownload } from "react-csv";
import Button from "material-ui/Button";
import PropTypes from "prop-types";
import Checkbox from "material-ui/Checkbox";
import "react-virtualized/styles.css";
import "../../styles/App.css";
import styles from "./Table.example.css";

export default class PartTable extends PureComponent {
  constructor(props) {
    super(props);

    const sortBy = "partNumber";
    const sortDirection = SortDirection.ASC;
    const sortedList = this._sortList({ sortBy, sortDirection });

    this.state = {
      list: this.props.list,
      sortBy,
      sortDirection,
      sortedList,
      rowCount: 1000
    };
    this._noRowsRenderer = this._noRowsRenderer.bind(this);
    this._generateCheckbox = this._generateCheckbox.bind(this);
    this._sort = this._sort.bind(this);
  }

  render() {
    const { headers } = this.props;
    const { list, sortBy, sortDirection, sortedList, rowCount } = this.state;

    const rowGetter = ({ index }) => this._getDatum(sortedList, index);

    return (
      <div>
        <AutoSizer disableHeight>
          {({ width }) => (
            <Table
              width={width}
              height={500}
              headerHeight={50}
              rowHeight={50}
              rowClassName={this._rowClassName}
              rowCount={rowCount}
              rowGetter={({ index }) => list[index]} // ({ index }) => list[index]
              noRowsRenderer={this._noRowsRenderer}
              onHeaderClick={this._sortByClickedHeader}
              sort={this._sort}
              sortBy={sortBy}
              sortDirection={sortDirection}
            >
              {headers.map(header => {
                return (
                  <Column
                    key={header.id}
                    label={header.label}
                    dataKey={header.id}
                    width={100}
                    flexGrow={1}
                    cellRenderer={
                      header.index ? this._generateCheckbox : undefined
                    }
                  />
                );
              })}
            </Table>
          )}
        </AutoSizer>
        <CSVLink data={list}>
          <Button color="accent">Export {list.length} records to CSV</Button>
        </CSVLink>
      </div>
    );
  }

  _getDatum(list, index) {
    return list[index];
  }

  _sort({ sortBy, sortDirection }) {
    const sortedList = this._sortList({ sortBy, sortDirection });

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

  _sortList({ sortBy, sortDirection }) {
    const { list } = this.props;
    if (sortBy) {
      let updatedList =
        // sort by name
        list.sort(function(a, b) {
          var nameA = a[sortBy].toUpperCase(); // ignore upper and lowercase
          var nameB = b[sortBy].toUpperCase(); // ignore upper and lowercase
          if (nameA < nameB) {
            return -1;
          }
          if (nameA > nameB) {
            return 1;
          }
          // names must be equal
          return 0;
        });
      sortDirection === SortDirection.DESC
        ? updatedList.reverse()
        : updatedList;
    }
  }

  _noRowsRenderer() {
    return <div>No Parts here...</div>;
  }
  _generateCheckbox(event) {
    // console.log(event);
    return (
      <div className="table-check-box">
        {this.props.activeCheckboxes && (
          <Checkbox
            onChange={() => this.props._activeCheckbox(event.rowData._id)}
            checked={this.props.activeCheckboxes.includes(event.rowData._id)}
          />
        )}
        {event.cellData}
      </div>
    );
  }
}

PartTable.PropTypes = {
  list: PropTypes.arrayOf({}).isRequired,
  activeCheckboxes: PropTypes.arrayOf({}),
  _activeCheckbox: PropTypes.func,
  headers: PropTypes.arrayOf({}.isRequired)
};
Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.