简体   繁体   中英

Applying multiple filters to a table reactjs

在这里,我有三个过滤器可供选择,我需要从中过滤表格中的数据。

Here i have three filters on selection of which i need to filter data in a table. I am using if else statement to check and filter the data, hence i want to modify the code in some modular way to achieve the same can any one suggest me, should i go with switch case?

  if (mapFilter === 'Mapped') {
    if (listFilter) {
      const result = fullData.filter(
        data =>
          data.partner_mapping_classification.length > 0 &&
          data.account === listFilter,
      );
      setFinalData(result);
    } else {
      const result = fullData.filter(
        data => data.partner_mapping_classification.length > 0,
      );
      setFinalData(result);
    }
  } else if (mapFilter === 'Not Mapped') {
    if (listFilter) {
      const result = fullData.filter(
        data =>
          data.partner_mapping_classification === '' &&
          data.account === listFilter,
      );
      setFinalData(result);
    } else {
      const result = fullData.filter(
        data => data.partner_mapping_classification === '',
      );
      setFinalData(result);
    }
  } else if (mapFilter === 'All') {
    if (listFilter) {
      const result = fullData.filter(
        data => data.account === listFilter,
      );
      setFinalData(result);
    } else {
      const result = fullData.filter(
        data => data.partner_mapping_classification.length > 0,
      );
      setFinalData(result);
    }
  } else if (mapFilter === '' && listFilter !== '') {
    const result = fullData.filter(
      data => data.account === listFilter,
    );
    setFinalData(result);
  } else if (mapFilter === '' && listFilter === '') {
    setFinalData([]);
  } else {
    setFinalData([]);
  }
};

Easy to scale method (followed by live-demo)


Using switch statements or multiple chained if( statements (or, even, multiple conditions within same if( statement) doesn't seem to be a good idea, as scaling and maintaining such code will become way too difficult.

As the opposite to above mentioned hardcoding techniques, I would suggest to have an object within your table component's state that will bind object properties (you wish your table entries to get filtered by) to keywords (attached to your inputs).

Assuming (based on your screenshot) you use MaterialUI for styling your components, following example would demonstrate above approach:

 const { useState } = React, { render } = ReactDOM, { Container, TextField, TableContainer, Table, TableHead, TableBody, TableRow, TableCell } = MaterialUI, rootNode = document.getElementById('root') const sampleData = [ {id: 0, name: 'apple', category: 'fruit', color: 'green'}, {id: 1, name: 'pear', category: 'fruit', color: 'green'}, {id: 2, name: 'banana', category: 'fruit', color: 'yellow'}, {id: 3, name: 'carrot', category: 'vegie', color: 'red'}, {id: 4, name: 'strawberry', category: 'berry', color: 'red'} ], sampleColumns = [ {id: 0, property: 'name', columnLabel: 'Item Name'}, {id: 1, property: 'category', columnLabel: 'Category'}, {id: 2, property: 'color', columnLabel: 'Item Color'} ] const MyFilter = ({filterProperties, onFilter}) => ( <Container> { filterProperties.map(({property,id}) => ( <TextField key={id} label={property} name={property} onKeyUp={onFilter} /> )) } </Container> ) const MyTable = ({tableData, tableColumns}) => ( <TableContainer> <Table> <TableHead> <TableRow> { tableColumns.map(({id, columnLabel}) => ( <TableCell key={id}> {columnLabel} </TableCell> )) } </TableRow> </TableHead> <TableBody> { tableData.map(row => ( <TableRow key={row.id}> { tableColumns.map(({id, property}) => ( <TableCell key={id}> {row[property]} </TableCell> )) } </TableRow> )) } </TableBody> </Table> </TableContainer> ) const App = () => { const [state, setState] = useState({ data: sampleData, columns: sampleColumns, filterObj: sampleColumns.reduce((r,{property}) => (r[property]='', r), {}) }), onFilterApply = ({target:{name,value}}) => { const newFilterObj = {...state.filterObj, [name]: value} setState({...state, filterObj: newFilterObj, data: sampleData.filter(props => Object.entries(newFilterObj).every(([key,val]) =>.val.length || props[key].toLowerCase().includes(val.toLowerCase())) ) }) } return ( <Container> <MyFilter filterProperties={state.columns} onFilter={onFilterApply} /> <MyTable tableData={state.data} tableColumns={state,columns} /> </Container> ) } render ( <App />, rootNode )
 <script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.12.0/umd/react.production.min.js"></script><script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.11.0/umd/react-dom.production.min.js"></script><script src="https://unpkg.com/@material-ui/core@latest/umd/material-ui.development.js"></script><div id="root"></div>

As Sudhanshu pointed out, you should create event listeners for all these select dropdowns and then update state based on that.

I created a small sample of how I would do it, but just be warned that this isn't tested and I just wrote it without actually running the code or anything. So it is buggy for sure in some regard.

const fullData = ['first', 'second', 'third'];

const BigFilter = () => {
  const [activeFilters, setActiveFilters] = useState([]);
  const [filteredValues, setFilteredValues] = useState([]);

  const handleFilterChange = (event) => {
    const { target } = event;

    const isInFilter = activeFilters.some((element) => element.name === target.name);

    if (!isInFilter) {
      setActiveFilters((currentState) => {
        return [...currentState, { name: target.name, value: target.value }];
      });
    } else {
      setActiveFilters((currentState) => {
        return [...currentState.filter((x) => x.name !== target.name), { name: target.name, value: target.value }];
      });
    }
  };

  useEffect(() => {
    // Just set full data as filtered values if no filter is active
    if (activeFilters.length === 0) {
      setFilteredValues([...fullData]);
      return;
    };

    let finalData = [...fullData];

    // Returns undefined if it cannot find the element with .name === 'list' in array, otherwise it will return that element
    const listData = activeFilters.find((element) => (element.name = 'list'));
    if (listData) {
      // Do some filtering for first select/dropdown
      const { value } = listData;
      // value is the value of your select dropdown that was selected
      finalData = finalData.filter((x) => x.something > 0);
    }

    // Returns undefined if it cannot find the element with .name === 'list' in array, otherwise it will return that element
    const statusData = activeFilters.find((element) => (element.name = 'status'));
    if (statusData) {
      // Do some filtering for second select/dropdown
      const { value } = statusData;
      // value is the value of your select dropdown that was selected
      finalData = finalData.filter((x) => x.something > 0);
    }

    // Returns undefined if it cannot find the element with .name === 'list' in array, otherwise it will return that element
    const amountData = activeFilters.find((element) => (element.name = 'amount'));
    if (amountData) {
      // Do some filtering for third select/dropdown
      const { value } = amountData;
      // value is the value of your select dropdown that was selected
      finalData = finalData.filter((x) => x.something > 0);
    }

    setFilteredValues(finalData);
    // You can go with multiple if statements to filter everything step by step
  }, [activeFilters]);

  return (
    <>
      <select name="list" onChange={handleFilterChange}>
        <option>List Option 1</option>
      </select>
      <select name="status" onChange={handleFilterChange}>
        <option>Status Option 1</option>
      </select>
      <select name="amount" onChange={handleFilterChange}>
        <option>Amount Option 1</option>
      </select>
      <div>
        {/* Render filtered values */}
        {filteredValues.map((singleValue) => singleValue.name)}
      </div>
    </>
  );
};

The basic idea here is that all your <select> elements react to the same event listener, making it easier to coordinate.

You got two basic arrays as state ( activeFilters and filteredValues ). When onChange handler is triggered, you check the name of the filter and check if that filter is already present in your activeFilters state. If it isn't, you add its name and value to that state. That's why I used name="some name" on each <select> in order to identify it somehow. In other case, if the filter is already present in that state, we remove it and just add its entry again with the new value. (This can probably be written way better, but it's just to give you an idea.)

Both of these cases set new state for active filters with setActiveFilter . Then we have the useEffect hook below which filters all the data based on active filters. As you can see it has that dependency array as a second argument and I added activeFilters variable to it so that every time activeFilters updates it will trigger all the logic in useEffect and it will change your filteredValues .

The logic in useEffect will go step by step and check if each filter is active and filter data for each of them if they are active step by step. If the first filter is active it will filter data that's needed and store it again in finalData and then it will go to the second if statement and if the filter for that is active it will perform another filter, but now on already filtered data. In the end, you should get data that passes through all active filters. I'm sure there's a better way of doing this, but it's a start.

Btw, usually I wouldn't do this

finalData = finalData.filter((x) => x.something > 0);

Re-assigning the same variable with filtered data from it, but I would say it's ok in this case since that finalData variable was created in that useEffect scope and it cannot be mutated from outside the scope. So it's easy to track what it is doing.

I'm sorry if this doesn't work, but it might guide you to your solution.

You can add a filter to the fullData array and provide the value of each of the dropdowns to the filter function

fullData.filter(element => {
    return element.account == first && element.account == second && element.account == third;
});

You can also put in checks for the filters, like if the value is just '' then return false ie return the whole array else return the filtered list

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM