Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Force React-table to reload #955

Closed
IPessers opened this issue May 8, 2018 · 23 comments
Closed

Force React-table to reload #955

IPessers opened this issue May 8, 2018 · 23 comments

Comments

@IPessers
Copy link

IPessers commented May 8, 2018

What version of React-Table are you using?

6.8.0

What bug are you experiencing, or what feature are you proposing?

I'd like to be able to force the Component to reload so the 'onFetchData' handler would trigger again, or be able to reuse the function implemented inside the 'onFetchData'.

The context is that I have a function that add a new object in my data base, which should appear in the table, but it needs to update to do so. I can't just insert it my state.data since my backend alter the object.

It's my first project with React, maybe I'm missing a simple concept.

Use https://codesandbox.io/s/X6npLXPRW (by clicking the "Fork" button) to reproduce the issue.

https://codesandbox.io/s/olo47vwo89

Thank you for your time

@gary-menzel
Copy link
Contributor

React refreshes based on properties on the component changing.

If you are using onFetchData you have to use this.setState to set a state variable with the data and then use that as the data prop for ReactTable. This is how 99% of React components work.

@IPessers
Copy link
Author

IPessers commented May 9, 2018

@gary-menzel Thanks for your answer, but it's not solving the problem.

Maybe I didn't explain the problem the correctly.

I have a custom component which wrappe a ReactTable component. React-Table is to retrieve my data on database in the backend.
My custom component also has a handler to add an object in my database and upon success, I notify my user by altering the state of the custom component.

The problem is, upon successful add of an object, the component ReactTable becomes outdated and does not refresh, even though I change the state of my custom component.

My apologies for not being clear enough on the first post and thank you for your time. :)

@gary-menzel
Copy link
Contributor

If you search these github issues you will see that many people have asked for a "refresh" function - and there is not going to be one added because it just isn't the way React works.

It's not the state of your custom component that needs to change - it is the properties that are being used by ReactTable. So, to get the data to update, you have to use this.setState to change the data prop in your wrapping components state. That is how "React" does updates - that is the simple concept that you have to wrap your head around.

Even more so, ReactTable stretches those concepts even further (so if it is your first project, trying to get ReactTable to work like other JS packages you may have used with something like jQuery will be a challenge and you'll just have to work through it until you "get it" - no shortcuts).

Finally, everyone coming from that "call a function to do something" approach wants to use onFetchData - I think it is unnecessary 99% of the time (and usually only needed when you have hundreds of thousands of records).

The codesandbox you supplied doesn't actually work (it gets compilation errors) so I was unable to see what your component architecture looks like. Feel free to post a "working" (aside from the problem you described) replacement.

@IPessers
Copy link
Author

IPessers commented May 9, 2018

Thank you, I was missing one point in React, the re render does not take effect on child components unless state changing.

Thank you for explaining this to me, now it works, following your instructions: when the creation succeed, I call my database to read the new object and manually add it to the data.

I was talking about the onFetchData because it's the one used in the example.

Thank you again for the great support.

@IPessers IPessers closed this as completed May 9, 2018
@yixing-liu
Copy link

Correct me if I am wrong. I think this only works when the length of data array has changed. If you just change one element in the array, for example, data[0].age = 3; then setState({data}), it will not update.

@yixing-liu
Copy link

react-virtualized does provide a forceUpdate function to force the grid to update. It's useful because when the underlying data is changed frequently, we want to forceUpdate periodically (e.g. every second) instead of asynchronously (e.g. onUpdate).

@nolatone
Copy link

nolatone commented Nov 6, 2018

@gary-menzel, earlier in this thread you responded with this:

"React refreshes based on properties on the component changing.
If you are using onFetchData you have to use this.setState to set a state variable with the data and then use that as the data prop for ReactTable. This is how 99% of React components work."

In my case I'm using redux for my data so I'm using this.props. vs this.state. In my testing it seems to make no difference which I use, but I'm having an issue where the state in ReactTable component is not updated...

I'm using redux to store multiple "views" of logging data, each view also stores the filtered object from table state based on what filters, if any are selected when that view is selected. If there are no filters set, filtered = []. When I render ReactTable, I pass this.props.filtered (from redux mapStatetoProps) in via filtered and defaultFiltered props so the table renders with the filters for that view.

The problem I'm having is that if I have a view that DOES have filters (table state.filtered NOT empty), then I create a new view that has filtered = [], the next render still shows the filters of the last view, which I consider "stale". The new data is displayed as expected, it's just the filters from the last view displayed.

Any thoughts on why that is, and how I can assure the new view/filtered object are reflected in the next render?

Thanks in advance,

Paul

@bvandenbon
Copy link

@gary-menzel I do agree that in react it is expected that a refresh only happens when the props change. But the thing is even that is not enough for the ReactTable component.

Let's consider:

const data = [ { name: "record1"} , { name:"record2"}  ];
this.setState( { data } );

render () { return <ReactTable data={this.state.data} ... /> };

Now, just following your logic, it would be enough to say:

const newData = [... this.state.date];
this.setState( { data: newData } );

But that isn't enough to force a refresh. You actually need to ...

const data= this.state.data;
const newData = [];
for (const record of data) {
  const newRecord = {... record};
  newData.push(newRecord);
}
this.setState( { data: newData });

And while I can understand it from a technical point of view. Yes, I can imagine why it's supposed to be like that. But let's document it somewhere.

@cdaringe
Copy link

cdaringe commented Jan 6, 2019

react-table owns state and hides it from us. thus, we can't simply "pass new data in" to get a refresh. my new data is a function of react-table's managed state.

thus, we at least need to some mechanism or prop to pass down into react-table to have it trigger it's data retrieval workflow. e.g. <ReactTable isDataStale />. such a prop being set perhaps kicks off onFetchData once, until that prop is cleared?

@tannerlinsley
Copy link
Collaborator

tannerlinsley commented Jan 6, 2019 via email

@lloydw
Copy link

lloydw commented Jan 10, 2019

@tannerlinsley is this accessible on master now?

@ghost
Copy link

ghost commented Jun 12, 2019

This answer is only satisfactory if you happen to have the data entity that was saved to your data store. If you made a POST call to a server, and it saves data, and that data is manipulated on the back end, you need to be able to update the table with the new field from the store. If you are using the onFetchData functionality, you have no way of forcing the table to call that function without artificially setting some state data to manipulate the react elements. You can't separate the data call function from the table, because then you will not have the tables state and instance data that it passes to onFetchData. The concept that this control does not need a refresh because "that's not how React works" is just false.

@tannerlinsley
Copy link
Collaborator

@cfalone, you are correct in your use case which is the very use case that is being taken into heavy consideration for v7.

@lloydw Yes.

@ghost
Copy link

ghost commented Jun 12, 2019

You are fast!
What is a decent workaround for this, I need to get some refreshes to work yesterday? :)

@ghost
Copy link

ghost commented Jun 12, 2019

OK, here it is for V6:

  • add a ref to react table
  • create a function for onFetchData that receives table 'state' which will call your data and set data and paging, etc
  • get state from ref.current.state after your edit and call your fetch function with it, the state updates in your fetch calls promise return will update the table just like when it calls onFetchData itself.
  constructor(props) {
    super(props);
    this.table = React.createRef();
    this.fetch = this.fetch.bind(this);
  }
  render() {
    return <ReactTable
       ref={this.table}
       onFetchData={this.fetch}
       ...
    />;
  }
  fetch(state){
    // make call -> then update state
  }
  onFinishedEditOrDelete(){
     this.fetch(this.table.current.state);
  }
}

@somnaderi
Copy link

@cfalone Thank you so much for this.

A complementary piece to this that was helpful for me was that setState has a 2nd argument for a callback function to ensure that the fetch() call happens after the state change (if there are elements to your state that affect your fetch query).

So you can do:

this.setState({whatever state change}, this.fetch(this.table.current.state));

@mckennapaul27
Copy link

OK, here it is for V6:

  • add a ref to react table
  • create a function for onFetchData that receives table 'state' which will call your data and set data and paging, etc
  • get state from ref.current.state after your edit and call your fetch function with it, the state updates in your fetch calls promise return will update the table just like when it calls onFetchData itself.
  constructor(props) {
    super(props);
    this.table = React.createRef();
    this.fetch = this.fetch.bind(this);
  }
  render() {
    return <ReactTable
       ref={this.table}
       onFetchData={this.fetch}
       ...
    />;
  }
  fetch(state){
    // make call -> then update state
  }
  onFinishedEditOrDelete(){
     this.fetch(this.table.current.state);
  }
}

Legend! Wish I found this post about 7 hours ago!!

@ghost
Copy link

ghost commented Nov 9, 2019

OK, here it is for V6:

  • add a ref to react table
  • create a function for onFetchData that receives table 'state' which will call your data and set data and paging, etc
  • get state from ref.current.state after your edit and call your fetch function with it, the state updates in your fetch calls promise return will update the table just like when it calls onFetchData itself.
  constructor(props) {
    super(props);
    this.table = React.createRef();
    this.fetch = this.fetch.bind(this);
  }
  render() {
    return <ReactTable
       ref={this.table}
       onFetchData={this.fetch}
       ...
    />;
  }
  fetch(state){
    // make call -> then update state
  }
  onFinishedEditOrDelete(){
     this.fetch(this.table.current.state);
  }
}

Man, you are a hero :D

@jordansoltman
Copy link

Any update on how to do this in v7. Looked through the docs and I'm not seeing anything...

@ghost
Copy link

ghost commented Feb 27, 2020

Been using redux and had the same issue, to force update of the component and to trick it that new set of data is being retrieved (get around the referential checks) I have resorted to recreating the data array by simply using JSON.parse() and JSON.stringify() before passing it to the action.

handleRemoveFile = id => {
    var files = this.props.download.files;
    _.remove(files, file => file.id === id);
    var newfiles = JSON.parse(JSON.stringify(files));
    this.props.setDownloadFiles(newfiles);
};

For now it works, really wishing to have a more decent way to force the reload/refresh.

@artemis-prime
Copy link

@tannerlinsley , could you please refer to the change the was made in v7? I'm having this issue in today (Nov 2021) and would love to update my pattern to the legit way :)

@handhikadj
Copy link

please reopen this issue

@istvandesign
Copy link

It's very easy to get into chicken and egg situations with filters or preselected rows from previous data.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests