【问题标题】:Applying multiple filters to a table reactjs将多个过滤器应用于表reactjs
【发布时间】:2020-09-03 23:54:30
【问题描述】:

在这里,我有三个过滤器可供选择,我需要过滤表中的数据。 我正在使用 if else 语句来检查和过滤数据,因此我想以某种模块化方式修改代码以实现相同的效果,任何人都可以建议我,我应该使用 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([]);
  }
};

【问题讨论】:

    标签: javascript reactjs react-hooks jsx


    【解决方案1】:

    易于扩展的方法(随后是现场演示)


    使用switch 语句或多个链接的if( 语句(或者,甚至在同一if( 语句中的多个条件)似乎不是一个好主意,因为扩展和维护这样的代码将变得太困难了.

    与上面提到的硬编码技术相反,我建议在表格组件的状态中使用一个对象,它将对象属性(您希望您的表格条目被过滤)绑定到关键字(附加到您的输入)。

    假设(根据您的屏幕截图)您使用 MaterialUI 来设置组件的样式,以下示例将演示上述方法:

    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
    )
    &lt;script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.12.0/umd/react.production.min.js"&gt;&lt;/script&gt;&lt;script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.11.0/umd/react-dom.production.min.js"&gt;&lt;/script&gt;&lt;script src="https://unpkg.com/@material-ui/core@latest/umd/material-ui.development.js"&gt;&lt;/script&gt;&lt;div id="root"&gt;&lt;/div&gt;

    【讨论】:

      【解决方案2】:

      正如 Sudhanshu 指出的那样,您应该为所有这些选择下拉菜单创建事件侦听器,然后根据它更新状态。

      我创建了一个小示例来说明我将如何做到这一点,但只是被警告说这没有经过测试,我只是在没有实际运行代码或任何东西的情况下编写它。所以在某些方面肯定是有问题的。

      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>
          </>
        );
      };
      

      这里的基本思想是所有&lt;select&gt; 元素都对同一个事件侦听器作出反应,从而更容易协调。

      您有两个基本数组作为状态(activeFiltersfilteredValues)。当onChange 处理程序被触发时,您检查过滤器的名称并检查该过滤器是否已经存在于您的activeFilters 状态中。如果不是,则将其名称和值添加到该状态。这就是为什么我在每个&lt;select&gt; 上使用name="some name" 以便以某种方式识别它。在其他情况下,如果过滤器已经存在于该状态,我们将其删除并使用新值再次添加其条目。 (这可能会写得更好,但这只是为了给你一个想法。)

      这两种情况都使用setActiveFilter 为有源过滤器设置了新状态。然后我们在下面有useEffect 钩子,它根据活动过滤器过滤所有数据。如您所见,它具有该依赖数组作为第二个参数,我向其中添加了activeFilters 变量,因此每次activeFilters 更新时,它都会触发useEffect 中的所有逻辑,它会改变您的filteredValues

      useEffect 中的逻辑将逐步检查每个过滤器是否处于活动状态,如果它们处于活动状态,则逐步过滤每个过滤器的数据。如果第一个过滤器处于活动状态,它将过滤所需的数据并将其再次存储在 finalData 中,然后它将转到第二个 if 语句,如果该过滤器处于活动状态,它将执行另一个过滤器,但现在已经过滤数据。最后,您应该获得通过所有活动过滤器的数据。我确信有更好的方法可以做到这一点,但这是一个开始。

      顺便说一句,我通常不会这样做

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

      使用从中过滤的数据重新分配相同的变量,但我想说在这种情况下没关系,因为 finalData 变量是在 useEffect 范围内创建的,并且不能从范围外进行变异。所以很容易跟踪它在做什么。

      如果这不起作用,我很抱歉,但它可能会指导您找到解决方案。

      【讨论】:

        【解决方案3】:

        您可以向 fullData 数组添加过滤器,并将每个下拉列表的值提供给过滤器函数

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

        您还可以检查过滤器,例如如果值只是 '' 然后返回 false 即返回整个数组,否则返回过滤后的列表

        【讨论】:

        • 我已经更新了我为两个过滤器所做的代码
        • 为了使代码更具可读性,您可以做的另一件事是,为所有下拉列表创建侦听器,然后在下拉列表值更改时过滤状态。
        • 是的,我在想,如果你看到我在下拉列表中的值很少,因为我必须过滤我正在考虑的数据来编写一个开关盒,这将是一个好主意?
        猜你喜欢
        • 2014-10-21
        • 1970-01-01
        • 1970-01-01
        • 2017-10-04
        • 2017-10-27
        • 1970-01-01
        • 1970-01-01
        • 2020-02-02
        • 1970-01-01
        相关资源
        最近更新 更多