Reputation: 262
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([]);
}
};
Upvotes: 0
Views: 8963
Reputation: 15530
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>
Upvotes: 7
Reputation: 313
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.
Upvotes: 2
Reputation: 392
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 i.e return the whole array else return the filtered list
Upvotes: 0