Start by breaking down the problem into smaller parts.
Your data structure is:
const items = [
{ id: 1, brand: 'Acer', title: 'Acer 1', series: "1" },
{ id: 2, brand: 'Acer', title: 'Acer 2', series: "1" },
{ id: 3, brand: 'Asus', title: 'Asus 1', series: "3" },
{ id: 4, brand: 'Acer', title: 'Acer 3', series: "1" },
{ id: 5, brand: 'Apple', title: 'Apple 1', series: "3" },
{ id: 6, brand: 'Acer', title: 'Acer 4', series: "3" },
{ id: 7, brand: 'Apple', title: 'Apple 2', series: "1" },
{ id: 8, brand: 'Asus', title: 'Asus 2', series: "2" },
{ id: 9, brand: 'Asus', title: 'Asus 3', series: "2" },
{ id: 10, brand: 'Acer', title: 'Acer 5', series: "2" },
{ id: 11, brand: 'Apple', title: 'Apple 3', series: "2" },
]
You want to filter it based on brand and series.
So that'd by 2 filter functions.
Finally, you need a list of filtered ids/objects. I'd suggest that you convert this into an object, but we can do it with an array too.
The filter functions would look something like:
const filterByBrand = (selectedBrands, allItems) => allItems.filter((item) => selectedBrands.indexOf(item.brand) > -1)
const filterBySeries = (selectedSeries, allItems) => allItems.filter((item) => selectedSeries.indexOf(item.series) > -1)
Now these can be used in a useEffect:
const [items, setItems] = useState(allItems)
const [filteredItems, setFilteredItems] = useState(allItems)
const [selectedBrands, setSelectedBrands] = useState([])
const [selectedSeries, setSelectedSeries] = useState([])
useEffect(() => {
let filtered = filterByBrands(selectedBrands, items) // first filtering by brands
filtered = filterBySeries(selectedSeries, filtered) // then filtering the results by series
setFilteredItems(filtered) // finally, setting it as a state variable
}, [items, selectedBrands, selectedSeries])
At this point, we have decoupled the filtering logic.
Now we can easily add logic to modify the selectedBrands and selectedSeries.
Bonus
This part is not tested, so you might have to do some refactoring.
Our checkbox could look like:
const Checkbox = ({ onClick, name }) => {
const [selected, setSelected] = useState(false)
const onCheckboxClick = () => {
setSelected(prev => !prev)
onClick()
}
return <input
name={name}
type="checkbox"
checked={selected}
onChange={onCheckboxClick} />
}
Here we are lifting the state up.
Next, we need to render these checkboxes for brands and series.
const BrandCheckboxes = ({ brands, setSelected }) => {
const onBrandCheckboxClick = (brand) => {
setSelected(prev => prev.indexOf(brand) > -1? [...prev.filter(b => b !== brand)] : [...prev, brand])
}
return
brands.map(brand => <Checkbox key={brand} name={brand} onClick={() => onBrandCheckboxClick(brand)} />)
}
The same thing can be done for series.
Finally, we can do something like:
const allItems = [
{ id: 1, brand: 'Acer', title: 'Acer 1', series: "1" },
{ id: 2, brand: 'Acer', title: 'Acer 2', series: "1" },
{ id: 3, brand: 'Asus', title: 'Asus 1', series: "3" },
{ id: 4, brand: 'Acer', title: 'Acer 3', series: "1" },
{ id: 5, brand: 'Apple', title: 'Apple 1', series: "3" },
{ id: 6, brand: 'Acer', title: 'Acer 4', series: "3" },
{ id: 7, brand: 'Apple', title: 'Apple 2', series: "1" },
{ id: 8, brand: 'Asus', title: 'Asus 2', series: "2" },
{ id: 9, brand: 'Asus', title: 'Asus 3', series: "2" },
{ id: 10, brand: 'Acer', title: 'Acer 5', series: "2" },
{ id: 11, brand: 'Apple', title: 'Apple 3', series: "2" },
]
const filterByBrand = (selectedBrands, items) => items.filter((item) => selectedBrands.indexOf(item.brand) > -1)
const filterBySeries = (selectedSeries, items) => items.filter((item) => selectedSeries.indexOf(item.series) > -1)
const FilteredItems = () => {
const [items, setItems] = useState(allItems)
const [filteredItems, setFilteredItems] = useState(allItems)
const [selectedBrands, setSelectedBrands] = useState([])
const [selectedSeries, setSelectedSeries] = useState([])
const [brands, setBrands] = useState([...new Set(allItems.map(item => item.brand))])
const [series, setSeries] = useState([...new Set(allItems.map(item => item.series))])
useEffect(() => {
let filtered = filterByBrands(selectedBrands, items) // first filtering by brands
filtered = filterBySeries(selectedSeries, filtered) // then filtering the results by series
setFilteredItems(filtered) // finally, setting it as a state variable
}, [items, selectedBrands, selectedSeries])
return (
<>
<BrandCheckboxes brands={brands} setSelected={setSelectedBrands} />
<SeriesCheckboxes brands={brands} setSelected={setSelectedBrands} />
{filteredItems.map((item) => (
<div key={item.id}>
<pre>
{JSON.stringify(item, null, 2)}
</pre>
</div>
))}
</>
)
}
Update:
The filter in useEffect is like an AND gate, it will only give you items which match both filter. If you want an OR gate, or if you want items which match either of the filters, you would do something like:
useEffect(() => {
const filteredByBrands = filterByBrands(selectedBrands, items) // first filtering by brands
const filteredBySeries = filterBySeries(selectedSeries, items) // then filtering the results by series
const filteredIds = [...filteredByBrands.map(i => i.id), ...filteredBySeries.map(i => i.id)]
const uniqueIds = [...new Set(filteredIds)]
const filtered = uniqueIds.map(id => items[id - 1]) // since you incremented the ids
setFilteredItems(filtered) // finally, setting it as a state variable
}, [items, selectedBrands, selectedSeries])