Do we have any function or example how we can have infinite scroll? And how can we display "There is no content" if no data is fetch?
for there being no content, we are handling that in the component that renders the grid, so basically:
React.createClass({
render: () => {
if(this.state.data,length) {
return (<ReactDataGrid {...props} />)
}
else {
return (<div>Nothing to see here</div>)
}
}
})
but if you think having an overridable placeholder makes sense we would probably consider that (and definitely take a pull request)
For infinite scroll, not something we do in production with this, but in theory that would mostly just be handled in your rowGetter, so just fetch once you get a request that in within your buffer, and update the grid.rowsCount as you expand, adding a buffer of some form. But imagine there will be a few tweaks needed to get this fully up and running, so ping us as you go
probably easiest on the slack channel, right @malonecj ?
My use case is to show a "Load more" button below the last row. When user scroll down, reaching the last row, he can click the "Load more" button to fetch more data. As for me, both "Load more" button and "There is no content" can be implemented by supporting table footer, on which can place these elements.
Probably the easiest solution is just add a row at the end which has a custom rowRenderer that says "Load More" and a click handler to kick off the fetch. Once you know you have everything, just stop adding that.
At a future time we might add a dedicated footer region, in which case you could just put it there
Be happy to take a PR with this as an addon or just an example though
I know it's a little late, but for anyone finding this on google, I made a hack that lets you do infinite scrolling. It's based on the fact that rowGetter lets you know what rows it is looking at, so you can call a load more function when you're 50 (or any arbitrary number) rows from the bottom.
You might have to mess around with shouldComponentUpdate to make sure the thing doesn't rerender out from under you when you change rows.
import React, { PropTypes } from 'react';
import ReactDataTableGrid from 'react-data-grid';
class InfiniteScrollDataGrid extends React.Component {
static propTypes = {
rows: PropTypes.array.isRequired, // eslint-disable-line
rowsCount: PropTypes.number.isRequired,
loadNext: PropTypes.func.isRequired,
};
constructor(props) {
super(props);
this.state = {
calledMoreRowsAtSize: 0
};
}
render() {
return (
<ReactDataTableGrid
{...this.props}
rowGetter={(i) => {
const { rows, loadNext, rowsCount } = this.props,
{ calledMoreRowsAtSize } = this.state,
currentEndPoint = (rows.length - 1);
// Welcome to hack city.
/*
React-data-grid has no support for infinite scrolling. It does, however, have a rowGetter
that lets us know what it's currently looking at. We can use this to call fetchMoreRows.
Unfortunately, it can call the same index repeatedly, spamming the server. This is why
you see the calledMoreRowsAtSize variable. That indicates we've already launched a fetch
from this row size.
*/
// Fetch the next set of rows when we're 50 away from the end of the current set
const alreadyFetched = calledMoreRowsAtSize !== rows.length;
if (alreadyFetched && (i + 50) === currentEndPoint && currentEndPoint < (rowsCount - 1)) {
this.setState({ calledMoreRowsAtSize: rows.length });
loadNext();
}
return this.props.rows[i] || {};
}}
/>
);
}
}
export default InfiniteScrollDataGrid;
UPDATE: Cleaned up the code a little more:
import React, { PropTypes } from 'react';
import ReactDataTableGrid from 'react-data-grid';
class InfiniteScrollDataGrid extends React.Component {
static propTypes = {
rows: PropTypes.array.isRequired, // eslint-disable-line
onViewportChange: PropTypes.func.isRequired
};
constructor(props) {
super(props);
this.state = {
calledMoreRowsAtSize: 0
};
}
render() {
let lastRow = 0;
return (
<ReactDataTableGrid
{...this.props}
rowGetter={(i) => {
if (lastRow !== i - 1) {
this.props.onViewportChange(i);
}
lastRow = i;
return this.props.rows[i] || {};
}}
/>
);
}
}
export default InfiniteScrollDataGrid;
You can use onViewPortChange to start fetching when the top row is in the last page you have populated.
Hi, thanks for the idea to use rowGetter.
I use this pattern and get this warning:
Warning: setState(...): Cannot update during an existing state transition (such as within render or another component's constructor...
This true I perform an action from render function.
rowGetter(i) {
const { data } = this.props;
// do fetch 10 rows before end
if(data.length < i + 10 && this.prevDataLength != data.length){
if(this.infinityScrollAction){
const rowFetchLimit = data.length + 10;
this.prevDataLength = data.length;
this.infinityScrollAction(rowFetchLimit);
}
}
return this.getRows()[i];
}
did you have this warning also?
Hi, I was searching to implement the infinite scroll whenever the vertical scroll bar reached the halfway of the length, in order to prepare to make the new request and fetch the data, and I did it as follows after spending a lot of days inspecting the code in the browser with chrome and reading the code and searching a solution in the internet:
let React = require('react');
let ReactDataGrid = require('react-data-grid');
const {Toolbar, Data:{Selectors}} = require('react-data-grid-addons');
class DataGridDemoComponent extends React.Component {
constructor(props, context) {
super(props, context);
this.rowOffsetHeight = 0;
let dummyRows = [];
for (let i = 0; i < 200; i++) {
dummyRows.push({col1: i, col2: "default " + i});
}
this.totalRowsFromTheBackendToDisplay = 1000;
this.rowsCount = dummyRows.length;
this.state = {rows: dummyRows, filters: {}, sortColumn: null, sortDirection: null};
this.rowGetter = this.rowGetter.bind(this);
this.handleGridSort = this.handleGridSort.bind(this);
this.handleFilterChange = this.handleFilterChange.bind(this);
this.onClearFilters = this.onClearFilters.bind(this);
this.getRows = this.getRows.bind(this);
this.getSize = this.getSize.bind(this);
this.onScroll = this.onScroll.bind(this);
this.columns = [
{ key: 'col1', name: 'Index', filterable: true, sortable: true, resizable: true },
{ key: 'col2', name: 'Data', filterable: true, sortable: true, resizable: true }
];
}
getRows() {
return Selectors.getRows(this.state);
}
getSize() {
return this.getRows().length;
}
rowGetter(rowIdx) {
const rows = this.getRows();
return rows[rowIdx];
}
handleGridSort(sortColumn, sortDirection) {
this.setState({ sortColumn: sortColumn, sortDirection: sortDirection });
}
handleFilterChange(filter) {
let newFilters = Object.assign({}, this.state.filters);
if (filter.filterTerm) {
newFilters[filter.column.key] = filter;
} else {
delete newFilters[filter.column.key];
}
this.setState({ filters: newFilters });
}
onClearFilters() {
this.setState({ filters: {} });
}
onScroll(e) {
let halfWayVScroll = parseInt(((this.rowsCount * this.rowOffsetHeight) / 2));
let currentVScroll = e.scrollTop;
if (currentVScroll >= halfWayVScroll) {
if (this.totalRowsFromTheBackendToDisplay > this.rowsCount) {
// simulate ajax request 200 rows --- fetch more
for (let i = 0; i < 200; i++) {
this.state.rows.push({
col1: this.rowsCount + i,
col2: "New " + (this.rowsCount + i)
});
}
this.setState({rows: this.state.rows});
this.rowsCount += 200;
}
}
}
render() {
let self = this;
return <div>
<ReactDataGrid
onGridSort={this.handleGridSort}
enableCellSelect={true}
columns={this.columns}
rowGetter={this.rowGetter}
rowsCount={this.getSize()}
minHeight={500}
toolbar={<Toolbar enableFilter={true}/>}
onAddFilter={this.handleFilterChange}
onClearFilters={this.onClearFilters}
minColumnWidth={150}
ref={(element) => {
if (element !== null) {
let base = element.base;
base.onScroll = this.onScroll;
self.rowOffsetHeight = element.getRowOffsetHeight();
}
}}
/>
</div>
}
}
RealUserStatComponent.propTypes = {};
export default DataGridDemoComponent;
totalRowsInFromTheBackendToDisplay I think should be the total count in the database - how would you set that in a real-world scenario?
They are the same, it refers to the total count, rows count from the backend, or database or other service or whatever outside source
I used this in my app, the above component is a child component of another one that will pass the props to the above one (the above component is re-written for demo purposes), to display a specified number of rows that are fetched by ajax request. I'm using flux pattern
Aha - I suspected it needed to be set before it rendered so it makes sense you have it in the properties and as a sub-component - i'll try that - thanks!
I want to point out that the important piece of the example @arkanmgerges provided is using the onScroll event to make necessary updates to retrieve more data. You cannot make any kind of state update using rowGetter since that appears to be a render function, which is why @GlebDolzhikov was having the setState(...) error.
Most helpful comment
Hi, I was searching to implement the infinite scroll whenever the vertical scroll bar reached the halfway of the length, in order to prepare to make the new request and fetch the data, and I did it as follows after spending a lot of days inspecting the code in the browser with chrome and reading the code and searching a solution in the internet: