Material-table: Filtering states reset upon rerendering remote table

Created on 21 Apr 2019  路  19Comments  路  Source: mbrn/material-table

Describe the bug
When re-rendering a remote table, all the filter states reset - meaning the search text gets reset.

To Reproduce
Steps to reproduce the behavior:

  1. https://codesandbox.io/embed/j2jo7j91ky
  2. Enter text into one text field
  3. The other filter goes away, but the text disappears

Expected behavior
The filter text should remain

Additional context
This is relevant because firestore has limitations as to how many conditions can be on a query - so you want to only allow the user to be able to filter certain fields

bug wontfix

Most helpful comment

The problem occurs due to passing columns via props to MaterialTable, e.g.
<MaterialTable columns={ [ {title:"Col", field"col"} ] }/>

What happens next is the object used for the column is getting recreated and allocated a new memory address. Hence it will lose all of its properties set by material-table. More specifically, the object is appended by the value tableData containing all of the information used for filtering.

The workaround for that is to assign these column objects to a state so these objects don't get recreated when passing column props. This would happen whenever the component renders.

let [columnObject] = useState({title: "col",field: "col"});
=>
<MaterialTable columns={ [ columnObject ] }/>

All 19 comments

Hi @jakeleventhal

This is not a bug. You used columns directly. I advise you to use them in parent components' state. So tableData of columns will not be disappear when you setState in data function.

Are you sure this isn't a bug? You can see here that my table has the state of my filters but doesn't display the 'nar' filter in its appropriate filter field. Disappears after the search happens.

Screen Shot 2019-05-13 at 2 26 32 PM

This is the problem i was having @dalenhughes

@dalenhughes

Can you share your code please? If possible with a codesandbox.

@mbrn When you say 'You used columns directly. I advise you to use them in parent components' state' what do you mean by 'you used columns directly'? Do you mean that we can't use a parent's components state to dynamically create the columns like this? Here is my render function:
`
```javascript
render() {
const { classes, title } = this.props;
const { data, headers, meta, columnNames, columnLinks } = this.state;

    let columns = _.zipWith(headers, columnNames, (h, n) => { return { title: h, field: n } });
    _.forEach(columns, c => {
        if (_.includes(columnLinks, c.title)) {
            c.render = (value) => {
                return (
                    <Link to={`/reports/${columnLinks[c.field].report}?per_page=20&page=1&filter{${columnLinks[c.field].column}}=${value}`}>{value}</Link>
                );
            };
        }
    });

    return (
        <div>
            <Paper className={classes.root}>
                <MaterialTable
                    title={title}
                    options={{
                        filtering: true,
                        columnsButton: true,
                    }}
                    data={this.handleTableChange}
                    columns={columns}
                />
            </Paper>
        </div>
    );
}

````

Okay I found the problem. Calling setState or this.props.history.push({ search}) anywhere inside Promise that you pass into the data attribute causes this effect. In the sandbox below I took @jakeleventhal 's same program and removed the setState calls from the Promise and the filter text stays.

https://codesandbox.io/s/keen-visvesvaraya-44neu

In my case, when I query my data I want to maintain it outside of the table. I also want my url to match the filtering I'm doing so I can create links that pre-filter the table. In my handleTableChange function I do a setState({ loading: true }) before I return the Promise and even that breaks it.

I think I'm having the same issue. I have a table full of stock symbols being updated with remote trading data. Filtering a specific column works only up to the moment before the next data refresh. Then, the data refreshes and the filter is cleared. I'm unclear on the solution above and I couldn't get the codesandbox example to work (to filter or update) - was anyone able to figure this out? Thanks!

I have an opposite problem kind of. I would like to add the Clear All filters button. But is there a method on tableRef that I can invoke for this operation to happen?

The problem occurs due to passing columns via props to MaterialTable, e.g.
<MaterialTable columns={ [ {title:"Col", field"col"} ] }/>

What happens next is the object used for the column is getting recreated and allocated a new memory address. Hence it will lose all of its properties set by material-table. More specifically, the object is appended by the value tableData containing all of the information used for filtering.

The workaround for that is to assign these column objects to a state so these objects don't get recreated when passing column props. This would happen whenever the component renders.

let [columnObject] = useState({title: "col",field: "col"});
=>
<MaterialTable columns={ [ columnObject ] }/>

@patricekaufmann thanks you so much, you saved me so much time!

@patricekaufmann is a true hero. Would be nice if material-table did shallow comparisons to make this unnecessary.

@patricekaufmann thanks man, perfect solution, saved my day.

Memoizing the columns works great for preserving the filter state PER column. But the search field still loses its value on a re-render. Any solution to this?

Ok guys, I'm fixing this, let's discuss a nicer solution there
@mbrn

I think I'm having the same issue. I have a table full of stock symbols being updated with remote trading data. Filtering a specific column works only up to the moment before the next data refresh. Then, the data refreshes and the filter is cleared. I'm unclear on the solution above and I couldn't get the codesandbox example to work (to filter or update) - was anyone able to figure this out? Thanks!

i am also facing this issue i am having remote data how can i solve this ?

columnObject

This is not usefull because i am using class component.

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions. You can reopen it if it required.

I also encountered this issue, that I solved as proposed here by putting my columns array in the state of the parent component.
Now the filtering and sorting is kept intact after data refresh but I have another problem now.

Some of my columns uses async fetched data, so I use the render property like this:

 {
      field: 'company',
      title: 'Company',
      render: ({ company }) => (
        <CopyField
          content={companiesById.get(company)?.name} // companiesById is a Map stored in the state, which is refreshed when the async data is ready
        />
      ),
    },

The companiesById updates don't trigger a rerender of the field if the columns are stored in the state too.
Does anyone have an idea that could help with this?

No solutions worked for me. I am setting my table data to re-render again on any row edit and that resets the sorting of the table. Any suggestions on this ??

Was this page helpful?
0 / 5 - 0 ratings

Related issues

ps1011 picture ps1011  路  3Comments

Mihier-Roy picture Mihier-Roy  路  3Comments

balibou picture balibou  路  3Comments

jlgreene2 picture jlgreene2  路  3Comments

terapepo picture terapepo  路  3Comments