React-table: Create a Custom FilterComponent

Created on 7 Aug 2017  路  6Comments  路  Source: tannerlinsley/react-table

@tannerlinsley when creating a custom filter component the function have the filter object and the onChange callback function. Can I pass another callback function to the filter component? I want to render a dropdown button next to the input which has values like "startWith", "contains" and so on. What could be the best option to do that.

Most helpful comment

I needed something like this as well. My solution was to create a separate function and component which are passed to each column using the 'filterMethod' and 'Filter' props.

My custom component maintains internal state containing the current filter type and value. When either change the callback method is called with the state as the object.

The function checks a filterType property of the value and switches the filtering method based on that. The filterValue property of the value is used to compare to row values.

Not sure if this is the best way, but it works!

_Note: I am using Material-UI components to render the selection. You'll need to include that library, or recreate the component's render method._

Filter Method

function filterMethod(filter, row) {
  // Pivoted rows won't contain the column. 
  //  If that's the case, we set the to true (allowing us to only filter on the current column)
  const rowValue = row[filter.id];
  if (!rowValue) {
    return true;
  }

  const filterValue = filter.value.filterValue || '';
  const filterType = filter.value.filterType;
  switch (filterType) {
    case 'contains':
      return rowValue.indexOf(filterValue) > -1;
    case 'starts-with':
      return rowValue.startsWith(filterValue);
    case 'ends-with':
      return rowValue.endsWith(filterValue);
    case 'matches':
      return rowValue === filterValue;
    case 'greater-than':
      return rowValue > filterValue;
    case 'less-than':
      return rowValue < filterValue;
    default:
      return true;
  }
}

Filter Component

class FilterInnerComponent extends React.Component {
  constructor(props) {
    super(props);

    this.state = {
      filterType: 'contains',
      filterValue: ''
    };

    this.changeFilterType = this.changeFilterType.bind(this);
    this.changeFilterValue = this.changeFilterValue.bind(this);
  }

  changeFilterType(event, index, filterType) {
    const newState = {
      ...this.state,
      filterType
    };
    // Update local state
    this.setState(newState);
    // Fire the callback to alert React-Table of the new filter
    this.props.onChange(newState);
  }

  changeFilterValue(event, filterValue) {
    const newState = {
      ...this.state,
      filterValue
    };
    // Update local state
    this.setState(newState);
    // Fire the callback to alert React-Table of the new filter
    this.props.onChange(newState);
  }

  render() {
    return (
      <div className="filter">
        <SelectField
          onChange={this.changeFilterType}
          style={{
            width: '100%',
            height: '40px',
            fontSize: '12px'
          }}
          value={this.state.filterType}
          autoWidth
        >
          <MenuItem value="contains" primaryText="Contains" />
          <MenuItem value="matches" primaryText="Matches" />
          <MenuItem value="starts-with" primaryText="Starts With" />
          <MenuItem value="ends-with" primaryText="Ends With" />
          <MenuItem value="greater-than" primaryText="Greater Than" />
          <MenuItem value="less-than" primaryText="Less Than" />
        </SelectField>
        <TextField
          onChange={this.changeFilterValue}
          style={{
            width: '100%',
            height: '40px',
            float: 'left',
            fontSize: '12px'
          }}
          value={this.state.filterValue}
        />
      </div>
    );
  }
}

All 6 comments

I needed something like this as well. My solution was to create a separate function and component which are passed to each column using the 'filterMethod' and 'Filter' props.

My custom component maintains internal state containing the current filter type and value. When either change the callback method is called with the state as the object.

The function checks a filterType property of the value and switches the filtering method based on that. The filterValue property of the value is used to compare to row values.

Not sure if this is the best way, but it works!

_Note: I am using Material-UI components to render the selection. You'll need to include that library, or recreate the component's render method._

Filter Method

function filterMethod(filter, row) {
  // Pivoted rows won't contain the column. 
  //  If that's the case, we set the to true (allowing us to only filter on the current column)
  const rowValue = row[filter.id];
  if (!rowValue) {
    return true;
  }

  const filterValue = filter.value.filterValue || '';
  const filterType = filter.value.filterType;
  switch (filterType) {
    case 'contains':
      return rowValue.indexOf(filterValue) > -1;
    case 'starts-with':
      return rowValue.startsWith(filterValue);
    case 'ends-with':
      return rowValue.endsWith(filterValue);
    case 'matches':
      return rowValue === filterValue;
    case 'greater-than':
      return rowValue > filterValue;
    case 'less-than':
      return rowValue < filterValue;
    default:
      return true;
  }
}

Filter Component

class FilterInnerComponent extends React.Component {
  constructor(props) {
    super(props);

    this.state = {
      filterType: 'contains',
      filterValue: ''
    };

    this.changeFilterType = this.changeFilterType.bind(this);
    this.changeFilterValue = this.changeFilterValue.bind(this);
  }

  changeFilterType(event, index, filterType) {
    const newState = {
      ...this.state,
      filterType
    };
    // Update local state
    this.setState(newState);
    // Fire the callback to alert React-Table of the new filter
    this.props.onChange(newState);
  }

  changeFilterValue(event, filterValue) {
    const newState = {
      ...this.state,
      filterValue
    };
    // Update local state
    this.setState(newState);
    // Fire the callback to alert React-Table of the new filter
    this.props.onChange(newState);
  }

  render() {
    return (
      <div className="filter">
        <SelectField
          onChange={this.changeFilterType}
          style={{
            width: '100%',
            height: '40px',
            fontSize: '12px'
          }}
          value={this.state.filterType}
          autoWidth
        >
          <MenuItem value="contains" primaryText="Contains" />
          <MenuItem value="matches" primaryText="Matches" />
          <MenuItem value="starts-with" primaryText="Starts With" />
          <MenuItem value="ends-with" primaryText="Ends With" />
          <MenuItem value="greater-than" primaryText="Greater Than" />
          <MenuItem value="less-than" primaryText="Less Than" />
        </SelectField>
        <TextField
          onChange={this.changeFilterValue}
          style={{
            width: '100%',
            height: '40px',
            float: 'left',
            fontSize: '12px'
          }}
          value={this.state.filterValue}
        />
      </div>
    );
  }
}

@Siaver, I think @scrawfor nailed it. You can render literally whatever you want in the Filter rendering for a column. Just build your filter component as you normally would with however many dropdowns and options you want, and stick it in as the Filter function/component of a column. As long as your custom filter component can call the filter's onChange property with the new filter, you're golden :)

If you have more questions or need help implementing, please join the slack channel :)

@scrawfor @tannerlinsley Thanks a lot for your help!

@scrawfor
Your solution solves my problem for the most part. Even Im implementing a similar approach. I think this should serve as a solid reference for anyone who is implementing filtering by a custom component.

@tannerlinsley do you still have a slack channel?

Not for react-table. I moved the community to spectrum.chat/react-table.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

danielmariz picture danielmariz  路  3Comments

mellis481 picture mellis481  路  3Comments

mlajszczak picture mlajszczak  路  3Comments

dwjft picture dwjft  路  3Comments

DaveyEdwards picture DaveyEdwards  路  3Comments