I'm updating two states where setProductsShow depends on setSelectedCategories. setSelectedCategories is updated first then setProductsShow is updated to render products.
There are plenty of solutions on React state updates and I've read other answers on the matter but I'm confused on the order of which the states update for my specific case.
[
{
"name": "Potato",
"category": "food"
},
{
"name": "iPhone",
"category": "tech"
}
]
[
{
"name": "tech",
"menuSelect": false
},
{
"name": "food",
"menuSelect": false
}
]
const [productsShow, setProductsShow] = useState(products)
const [selectedCategories, setSelectedCategories] = useState(menuItems) // Default all items selected
// Event is triggered by a change in menu selection; code not shown
function handleChangeMenu(event) {
let { name, checked } = event.target
// Update categories based on menu selection
setSelectedCategories(category => (
category.map(item => item.name == name ? {
...item,
menuSelect: checked
} : item)
))
// If category is selected, then push onto an array
let flatSelected = []
setSelectedCategories(c => {
flatSelected = c.map(selected => {
if (selected.menuSelect) {
flatSelected.push(selected.name)
}
})
return c // Return the state since we're only reading
})
console.log("FLATSELECTED")
console.log(flatSelected)
// Render products which are marked as TRUE in array of menu items "flatSelected[]"
setProductsShow(() => {
console.log("INSIDE SETPRODUCTSSHOW")
console.log(products.filter(product => flatSelected.includes(product.category)))
console.log("FLATSELECTED")
console.log(flatSelected)
products.filter(product => flatSelected.includes(product.category))
})
}
The outcome I get is console.log(products.filter(product => flatSelected.includes(product.category))) prints out an empty array.
The problem is that you are setting state and try to access the state before the component has been rerenderd. The second time you call setSelectedCategories categories hasn't had time to get updated.
Set state at the end of the function and it should work:
import { useState } from "react";
const products = [
{
name: "Potato",
category: "food",
},
{
name: "iPhone",
category: "tech",
},
];
const menuItems = [
{
name: "tech",
menuSelect: false,
},
{
name: "food",
menuSelect: false,
},
];
function App() {
const [productsShow, setProductsShow] = useState(products);
const [selectedCategories, setSelectedCategories] = useState(menuItems);
function handleChangeMenu(event) {
//mock the event
let { name, checked } = { name: "tech", checked: true };
//updating catagory
const newSelectedCategories = menuItems.map((category) =>
category.name === name ? { ...category, menuSelect: checked } : category
);
//updating products
const newProductsShow = products.filter((product) =>
newSelectedCategories.some(
({ name, menuSelect }) => name === product.category && menuSelect
)
);
// Update state based on menu selection
setSelectedCategories(newSelectedCategories);
setProductsShow(newProductsShow);
}
return (
<div>
<button onClick={handleChangeMenu}> Toogle tech </button>
{productsShow.map((product) => (
<div> {product.name}</div>
))}
</div>
);
}
export default App;