SeaWarrior404
SeaWarrior404

Reputation: 4167

How to filter a React table using custom filter component?

I have a React Table(https://react-table.js.org) which gets populated from the data coming from an API. Above the table I have created a filter component which consists of a dropdown with multiple options. Once specific parameters are selected from the dropdown, I need to filter the Table based on the selected parameters. I store the state of the filter component and the state of the table component separately. Since both the table and the filter are separate components , how to take the values from the filter component and filter the table? My Table is as follows:

<ReactTable
data={tableData}
noDataText="No Appointments"
loading={this.props.loading}
showPagination={false}
filterable
defaultFilterMethod={(filter, row) =>
  String(row[filter.id]) === filter.value}
columns={[
  {
    columns: [
      {
        sortable: false,
        filterable: false,
        Header: "Id",
        accessor: "resourceId",
        headerStyle: {
          background: '#ECEFF1',
        },
      },
      {
        sortable: false,
        filterable: false,
        Header: "Tenant Name",
        accessor: "Name",
        id: "Tenant Name",
        headerStyle: {
          background: '#ECEFF1',
        },
      },
 </ReactTable>

My filter component is as follows:

export default class PopoverExampleAnimation extends 
React.Component {

constructor(props) {
super(props);

this.state = {
  open: false,
  clicked: [],
  Id: '',
  tenantName: '',
};

this.handleRequestClose = this.handleRequestClose.bind(this);
this.getId = this.getId.bind(this);
this.getTenantName = this.getTenantName.bind(this);

}

handleTouchTap = (event) => {
// This prevents ghost click.
 event.preventDefault();

this.setState({
  open: true,
  anchorEl: event.currentTarget,
  });
 };

handleRequestClose = () => {
  this.setState({
  open: false,
  });
 };

getId = (Id) => {
    console.log(Id);
    this.setState({Id});
}

getTenantName = (tenantName) => {
 console.log(tenantName);
this.setState({tenantName});
}

render() {
return (
  <div>
    <RaisedButton
        onClick={this.handleTouchTap}
        label="FILTER"
        labelColor="#26A69A"
    />
    <Popover
        open={this.state.open}
        anchorEl={this.state.anchorEl}
        anchorOrigin={{ horizontal: 'left', vertical: 'bottom' }}
        targetOrigin={{ horizontal: 'left', vertical: 'top' }}
        onRequestClose={this.handleRequestClose}
        animation={PopoverAnimationVertical}
    >
      <Menu>
        <MenuItem
            primaryText={"NAME - " + this.state.tenantName}
            rightIcon={<ArrowDropRight />}
            menuItems={[
              <MenuItem 
              primaryText="Group 1" 
              onClick={() =>
                this.getTenantName('Group 1')
              }
              />,
              <Divider />,
              <MenuItem primaryText="Group 2"
              onClick={() =>
                this.getTenantName('Group 2')
              }
              />,
            ]}
        />

        <Divider />
        <MenuItem
            primaryText={"ID -   " + this.state.Id}
            rightIcon={<ArrowDropRight />}
            menuItemStyle={{ backgroundcolor: '#E0F2F1' }}
            menuItems={[
              <MenuItem primaryText="1" onClick={() =>
                this.getId('1')
              }
              />,
              <Divider />,
              <MenuItem primaryText="2" onClick={() =>
                this.getId('2')
              }
              />,
              <Divider />,
              <MenuItem primaryText="3" onClick={() =>
                this.getId('3')
              }
              />,
              <Divider />,
              <MenuItem primaryText="4" onClick={() =>
                this.getId('4')
              }
              />,
            ]}
        />
        <Divider />
        <RaisedButton
            label="APPLY"
            style={{ margin: 2, width: '60px' }}
            labelColor="#FAFAFA"
            backgroundColor="#26A69A"
        />
        <RaisedButton
            label="CANCEL"
            style={{ margin: 22, width: '60px' }}
            labelColor="#26A69A"
            onClick={() =>
              //this.getId(' ')
              this.handleRequestClose()
             }
        />

      </Menu>
    </Popover>
  </div>
  );
 }
}

Both the table and filter are separate components and Im not using Redux for state management.

Upvotes: 1

Views: 11566

Answers (1)

dubes
dubes

Reputation: 5524

My understanding of your setup, you have:

<FilterComponent /> <!-- Stores instructions for Filters to apply -->
<TableComponent /> <!-- Displays data -->

And you expect that when the user makes changes in the filter component, the table component MUST reflect the changes.

If the above is true, the recommended way to do so (without relying on state management libraries) is lifting the state to the least common ancestor (see official docs).

If you do not have a least common ancestor, do not hesitate introducing a container component instead.

This component should serve the following function:

  • It knows/stores the state of the FilterComponent or is able to consume the filter instructions from the FilterComponent
  • It holds the data & filteredData in its state. filteredData can be passed to the table component as Props
  • Every time it gets a signal from FilterComponent to "apply" a filter, it should filter the data (leading to change of filteredData in its state, which should make the table component re-render)

i.e. in your FilterComponent, when you click the Apply button, the internal state is lifted to the "container" component and that leads to recomputation of the data to be displayed in your TableComponent. The table should re-render itself when the data changes.

I hope it opens your mind to the possibilities and you can then best decide which component holds what state and what responsibilities.

Upvotes: 1

Related Questions