React-table: [Feature/Request] Set a fetchData delay!

Created on 5 Sep 2017  路  8Comments  路  Source: tannerlinsley/react-table

I am using and enjoying react-tables with me react/redux project, to which I recently added server-side pagination, sorting and filtering. The main issue is that I don't want nor need to make requests to my API as often as they are generated by onFetchData (typically, onKeyUp events on filters) and that puts a strain on our server. I have tried adding a 'lastQuery' time flag to my store in order to only fetch data if the last time I did was, say, 400ms ago, but that systematically requires a last fetchData action after the last to get the data, otherwise it never loads (ex: write a word on a filter, and it will not filter if the entire word was written in less than 400ms).

There is a very similar plug-in for the datatables (non-react) library, and I was wondering if someone would be interested in adding that functionality.

If you have a workaround, do tell me!
Thank you very much,

Alessandro

Most helpful comment

Thanks for the reply, Gary. I have attempted to implement a conditional switch but it does not work. I'm sure I'm doing something wrong here. Is this how you'd approach it:

EDIT: I read the documentation a little more about setState.. so I just converted this into an instance variable and now everything works. I've updated the example for anyone wondering how to achieve this.

class SomeComponent extends Component {
  constructor() {
    super();

    this.state = {
      data: [],
      pages: null,
    };

    this.filtering = false;

    this.onFilteredChange = this.onFilteredChange.bind(this);
    this.fetchStrategy = this.fetchStrategy.bind(this);

    this.fetchData = this.fetchData.bind(this);
    this.fetchDataWithDebounce = _.debounce(this.fetchData, 1500);
    // ^ debounced version of "fetchData"

  }

  fetchStrategy(tableState) {
    if(this.filtering) {
      return this.fetchDataWithDebounce(tableState)
    } else {
      return this.fetchData(tableState);
    }
  }

  onFilteredChange(column, value) {
    this.filtering = true; // when the filter changes, that means someone is typing
  }

  fetchData(tableState) {
    this.filtering = false  // we've arrived either debounced or not, so filtering can be reset
    };

    // fetch api...
  }

  render() {
    return (
          <ReactTable
            data={this.state.data}
            pages={this.state.pages}

            onFetchData={this.fetchStrategy}
            onFilteredChange={this.onFilteredChange}
            filterable />
    );
  }
}

All 8 comments

Maybe lodash's debounce function can solve your use case?

https://lodash.com/docs/4.17.4#debounce

I was looking for this functionality as well. I'm only using client side filtering, but it is still noticeably slow if you type quickly. _.debounce looks like it would solve this problem, but I think it is a common enough use case that it should be implemented by this library.

The library is very lightweight and I don't think it should implement debounce for several reasons. Firstly, it would require reliance on a specific debounce library. Secondly, deciding when it should and shouldn't be used will complicate the interface to the component (I have already found lots of places where I would use and where I wouldn't).

All my debounce is at a higher level than react-table.

I'm using a filterable ReactTable that queries the server for both filtering AND pagination. If I add debounce to my handleFetch function, it applies debounces to both filtering and pagination. Pagination should be "debounce-less." What is the strategy for handling this?

In my mind, there should be an optional, dedicated prop that filter can use for processing.

E.g.:

<ReactTable filterable
            onFetchData={handleFetch}
            onFilterData={handleDebouncedFetch}
/>

onFetchData - handles fetching for filtering
onFilterData - handles fetching for pagination, sorting, etc

@derekcannon If I were to require that, I'd just move my debounce higher and make the switch between them in the single callback. I take your point, but if that is the best way to go, then there would be a good argument for having a separate call for each of the different things (paging, filtering, sorting, expanding, etc.). Not saying that is "wrong" but it sort of would "break" a lot of existing implementations so is probably best left until a re-design of the underlying system.

Even so, a use can probably click the page change faster than the server can return the data - so I (personally) would probably debounce that as well to abort unnecessary server calls - YMMV.

Thanks for the reply, Gary. I have attempted to implement a conditional switch but it does not work. I'm sure I'm doing something wrong here. Is this how you'd approach it:

EDIT: I read the documentation a little more about setState.. so I just converted this into an instance variable and now everything works. I've updated the example for anyone wondering how to achieve this.

class SomeComponent extends Component {
  constructor() {
    super();

    this.state = {
      data: [],
      pages: null,
    };

    this.filtering = false;

    this.onFilteredChange = this.onFilteredChange.bind(this);
    this.fetchStrategy = this.fetchStrategy.bind(this);

    this.fetchData = this.fetchData.bind(this);
    this.fetchDataWithDebounce = _.debounce(this.fetchData, 1500);
    // ^ debounced version of "fetchData"

  }

  fetchStrategy(tableState) {
    if(this.filtering) {
      return this.fetchDataWithDebounce(tableState)
    } else {
      return this.fetchData(tableState);
    }
  }

  onFilteredChange(column, value) {
    this.filtering = true; // when the filter changes, that means someone is typing
  }

  fetchData(tableState) {
    this.filtering = false  // we've arrived either debounced or not, so filtering can be reset
    };

    // fetch api...
  }

  render() {
    return (
          <ReactTable
            data={this.state.data}
            pages={this.state.pages}

            onFetchData={this.fetchStrategy}
            onFilteredChange={this.onFilteredChange}
            filterable />
    );
  }
}

@derekcannon you're my hero

Thanks for the reply, Gary. I have attempted to implement a conditional switch but it does not work. I'm sure I'm doing something wrong here. Is this how you'd approach it:

EDIT: I read the documentation a little more about setState.. so I just converted this into an instance variable and now everything works. I've updated the example for anyone wondering how to achieve this.

class SomeComponent extends Component {
  constructor() {
    super();

    this.state = {
      data: [],
      pages: null,
    };

    this.filtering = false;

    this.onFilteredChange = this.onFilteredChange.bind(this);
    this.fetchStrategy = this.fetchStrategy.bind(this);

    this.fetchData = this.fetchData.bind(this);
    this.fetchDataWithDebounce = _.debounce(this.fetchData, 1500);
    // ^ debounced version of "fetchData"

  }

  fetchStrategy(tableState) {
    if(this.filtering) {
      return this.fetchDataWithDebounce(tableState)
    } else {
      return this.fetchData(tableState);
    }
  }

  onFilteredChange(column, value) {
    this.filtering = true; // when the filter changes, that means someone is typing
  }

  fetchData(tableState) {
    this.filtering = false  // we've arrived either debounced or not, so filtering can be reset
    };

    // fetch api...
  }

  render() {
    return (
          <ReactTable
            data={this.state.data}
            pages={this.state.pages}

            onFetchData={this.fetchStrategy}
            onFilteredChange={this.onFilteredChange}
            filterable />
    );
  }
}

Thank you sooo much!

Was this page helpful?
0 / 5 - 0 ratings

Related issues

kieronsutton00 picture kieronsutton00  路  3Comments

ocalde picture ocalde  路  3Comments

dwjft picture dwjft  路  3Comments

danielmariz picture danielmariz  路  3Comments

bdkersey picture bdkersey  路  3Comments