1

I have a situation where in I am having multiple on click events that are fired on a column header. There will be one event for filter dropdown and the other one for sorting. There is a filter icon , on click of which column filter options will be shown. And on click of the header, sorting should also happen.

Now whenever I click on the filter icon, both handlers are getting fired. Can someone help me with this.

On click of filter icon, only filter handler should fire

Help would be appreciated.

Sandbox: https://codesandbox.io/s/relaxed-feather-xrpkp

Parent

import * as React from "react";
import { render } from "react-dom";
import ReactTable from "react-table";
import "./styles.css";
import "react-table/react-table.css";
import Child from "./Child";
interface IState {
  data: {}[];
  columns: {}[];
}

interface IProps {}

export default class App extends React.Component<IProps, IState> {
  constructor(props: any) {
    super(props);
    this.state = {
      data: [
        { firstName: "Jack", status: "Submitted", age: "14" },
        { firstName: "Simon", status: "Pending", age: "15" },
        { firstName: "Pete", status: "Approved", age: "17" }
      ],
      columns: []
    };
  }

  handleColumnFilter = (value: any) => {
    console.log(value);
  };

  sortHandler = () => {
    console.log("sort handler");
  };

  componentDidMount() {
    let columns = [
      {
        Header: () => (
          <div onClick={this.sortHandler}>
            <div style={{ position: "absolute", marginLeft: "10px" }}>
              <Child handleFilter={this.handleColumnFilter} />
            </div>
            <span>First Name</span>
          </div>
        ),
        accessor: "firstName",
        sortable: false,
        show: true,
        displayValue: " First Name"
      },
      {
        Header: () => (
          <div onClick={this.sortHandler}>
            <span>Status</span>
          </div>
        ),
        accessor: "status",
        sortable: false
      }
    ];
    this.setState({ columns });
  }

  render() {
    const { data, columns } = this.state;
    return (
      <div>
        <ReactTable
          data={data}
          columns={columns}
          defaultPageSize={10}
          className="-striped -highlight"
        />
      </div>
    );
  }
}

const rootElement = document.getElementById("root");
render(<App />, rootElement);

Filter Component

import * as React from "react";
import { Icon } from "semantic-ui-react";
import "./styles.css";
interface IProps {
  handleFilter(val1: any): void;
}
interface IState {
  showList: boolean;
}
export default class Child extends React.Component<IProps, IState> {
  constructor(props: any) {
    super(props);
    this.state = {
      showList: false
    };
  }

  toggleList = () => {
    console.log("filter handler");
    this.setState(prevState => ({ showList: !prevState.showList }));
  };

  render() {
    let { showList } = this.state;
    let visibleFlag: string;
    if (showList === true) visibleFlag = "visible";
    else visibleFlag = "";
    return (
      <div>
        <div style={{ position: "absolute" }}>
          <div
            className={"ui scrolling dropdown column-settings " + visibleFlag}
          >
            <Icon className="filter" onClick={this.toggleList} />
          </div>
        </div>
      </div>
    );
  }
}


1 Answer 1

2

You just need event.stopPropagation(). This will isolate the event to only this specific execution-block. So now when you click on the filter-icon, it will only trigger the designated event-handler.

  toggleList = (event) => {
    event.stopPropgation()
    console.log("filter handler");
    this.setState(prevState => ({ showList: !prevState.showList }));
  };

You'll also need to use it here as well:

  handleValueChange = (event: React.FormEvent<HTMLInputElement>, data: any) => {
    event.stopPropagation()
    let updated: any;
    if (data.checked) {
      updated = [...this.state.selected, data.name];
    } else {
      updated = this.state.selected.filter(v => v !== data.name);
    }
    this.setState({ selected: updated });
  };

And here:

 passSelectionToParent = (event: any) => {
    event.preventDefault();
    event.stopPropagation()
    this.props.handleFilter(this.props.name, this.state.selected);
  };

Literally, anytime you have a click-event for a parent-markup and it has children mark-up that also have a click-event, you can use event.stopPropagation() to stop the parent click-event from firing.

Here's the sandbox: https://codesandbox.io/s/elated-gauss-wgf3t

Sign up to request clarification or add additional context in comments.

12 Comments

@vjr If you were to put this on toggleList() then the filter handler would be triggered. Seems to be working fine in my sandbox: codesandbox.io/s/strange-cherry-tu6yr
@vjr i don't know if its just me. but the speed to complile these sandboxes are ridiculously slow.
@vjr you're welcome. I've added a link to the sandbox as well, but it looks like you already updated your code :)
@vjr yup! just posted an answer to your new posy
@vjr oh yeah! I had editted it cuz I was not sure if you wanted to include or not include :). Initially there was no negation haha. Sorry I was confused. I will change it back.
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.