DEV Community

ztorstri
ztorstri

Posted on

Reuseable component logic

I am implementing a custom table that will have pagination, filtering, sorting, and other common features. I do not want to use an existing solution, both because this is a good exercise to get familiar with React and because I want the table tailored to my needs.

The issue I'm running into is with the filtering. What I want is to put the "does object pass filter" logic in the Filter; I've used this pattern successfully in other languages and it's very clean.

However, with React all of the logic has to go in the parent because the parent can't call methods on the child. So I'm stuck.

Here's what I want to do, roughly.

class FilterContainer extends Component {
  constructor(props) {
    super(props);

    this.toggle = this.toggle.bind(this);

    this.state = { isOpen: false };
  }

  toggle() {
    this.setState({ isOpen: !this.state.isOpen });
  }

  render() {
    return (
      <Fragment>
        <FaFilter id="filter-icon" />
        <Popover placement="right" isOpen={this.state.isOpen} target="filter-icon" toggle={this.toggle}>
          <PopoverHeader>Filter table</PopoverHeader>
          <PopoverBody>
            {this.props.children}
          </PopoverBody>
        </Popover>
      </Fragment>
    );
  }
};

class Filter extends Component {
  constructor(props) {
    super(props);

    this.setValue = this.setValue.bind(this);
  }

  setValue(event) {
    this.props.setValue(this.props.name, event.target.value);
  }

  // I want this as a method on the filter because I will have different types of
  // filters, and I don't want to duplicate this everywhere I use a filter
  passesFilter(obj){
    if (obj.hasownproperty(this.props.name)){
      if (obj[this.props.name].contains(this.props.value)){
        return true;
      }
    }
  }

  render() {
    return (
      <Fragment>
        <Label>
          {this.props.name}

          <Input
            id={this.props.name + "-value"}
            type="text"
            value={this.props.value}
            onChange={this.setValue} />
        </Label>
      </Fragment>
    );
  }
};

But now imagine instead of Filter, I had a StringFilter which could handle case sensitivity and regex, a BoolFilter which is just true/false, maybe a DateFilter, etc. Each one of those has the concept of "passes filter", and duplicating them in the DataTable as well as anywhere else I want to use them sucks.

Hopefully this makes sense, if not I can provide more detail.

Top comments (0)