cbll
cbll

Reputation: 7219

React fixed-data-table: Uncaught TypeError: this._dataList.getSize is not a function

I'm attempting to use this example by the React developers to make a search filter for a table.

I have the table working fine with data from my backend statically. I have taken out an array for "sample" data to get the search functionality working. But I'm having a difficult time wrapping my head around how they use the "fake data" to populate their table as seen here, in contrary to "just" populating it with a test array as I want to.

Here's my source code. I want to filter through the "firstName" column, just as in Facebook's example(For simplicity). The error stems from when getSize() is called... But I suspect the issue is something else.

class DataListWrapper {
    constructor(indexMap, data) {
        this._indexMap = indexMap;
        this._data = data;
    }

    getSize() {
        return this._indexMap.length;
    }

    getObjectAt(index) {
        return this._data.getObjectAt(
            this._indexMap[index],
        );
    }
}

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

        this.testDataArr = []; // An array.
        this._dataList = this.testDataArr;

        console.log(JSON.stringify(this._dataList)); // It prints the array correctly.


        this.state = {
            filteredDataList: new DataListWrapper([], this._dataList)
        };

        this._onFilterChange = this._onFilterChange.bind(this);
    }

    _onFilterChange(e) {
        if (!e.target.value) {
            this.setState({
                filteredDataList: this._dataList,
            });
        }

        var filterBy = e.target.value;
        var size = this._dataList.getSize();
        var filteredIndexes = [];
        for (var index = 0; index < size; index++) {
            var {firstName} = this._dataList.getObjectAt(index);
            if (firstName.indexOf(filterBy) !== -1) {
                filteredIndexes.push(index);
            }
        }

        this.setState({
            filteredDataList: new DataListWrapper(filteredIndexes, this._dataList),
        });
    }

    render() {

        var filteredDataList = this.state.filteredDataList;


        if (!filteredDataList) {
            return <div>Loading table..  </div>;
        }

        var rowsCount = filteredDataList.getSize();



        return (
            <div>
                <input onChange={this._onFilterChange} type="text" placeholder='Search for first name.. ' />
                {/*A table goes here, which renders fine normally without the search filter. */}
            </div>
        );
    }
}

export default NameTable

Upvotes: 1

Views: 880

Answers (2)

getSize() and getObjectAt() can only be called on a data object which implements these methods, such as the DataListWrapper object.

If you pass a plain data array to render() then it does not offer the getSize() and getElementAt() methods and the call to the methods will fail.

The original demo works because the FakeObjectDataListStore data is an object (a 'FakeObjectDataListStore') which implements the getSize and getObjectAt methods).

So easiest integration is to make sure the data passed in is an object that offer these methods. Based in my case on the 'examples/FilterExample' I found the easiest integration (after struggling with many bad ones) was to turn the existing 'helpers/FakeObjectDataListStore.js' into my own helpers/ObjectDataListStore.js (or chose your name) thus retaining the existing method wrapping structure and size params throughout the design. I then simply replaced the calls to the 'fake' component with references to my own non-wrapped local arrays of list rows. You can arrange your local data to be static, or dynamically loaded from whatever database environment you use. It was then easy to modify the _setFiltered() method to filter on something else than 'firstName'.

The cool thing with FixedDataTable is its ability to browse large lists, and that the developer can write own custom cell renderers for example displaying a progress bar, button or menu anywhere in a list row.

Upvotes: 0

Facundo La Rocca
Facundo La Rocca

Reputation: 3866

Your problem is in _onFilterChange method.

You are doing this:

var size = this._dataList.getSize();

this._dataList is just an array, that's why getSize() does not exist in that object.

If I'm not misundertanding you should do this:

var size = this.state.filteredDataList.getSize();

The same will happend inside the loop, you are doing this:

var {firstName} = this._dataList.getObjectAt(index);

when you should do this:

var {firstName} = this.state.filteredDataList.getObjectAt(index);

Your _onFilterChange method should look something like this:

_onFilterChange(e) {
    if (!e.target.value) {
      this.setState({
        filteredDataList: this._dataList,
      });
    }

    var filterBy = e.target.value;
    //var size = this._dataList.getSize();
    var size = this.state.filteredDataList.getSize();
    var filteredIndexes = [];
    for (var index = 0; index < size; index++) {
      //var {firstName} = this._dataList.getObjectAt(index);
      var {firstName} = this.state.filteredDataList.getObjectAt(index);
      if (firstName.indexOf(filterBy) !== -1) {
        filteredIndexes.push(index);
      }
    }

    this.setState({
      filteredDataList: new DataListWrapper(filteredIndexes, this._dataList),
    });
}

Upvotes: 1

Related Questions