Reputation: 325
I have a component Form which returns a component Menu that is conditionally rendered depending on a switch.
This component Menu has a bunch of children components, including an Accordion (whose items render a card each) and a SpeedDial (I'm using Material UI).
When the user clicks a specific SpeedDial action button, the accordion should present an additional item.
This is a summarized hierarchy:
Form <- Menu <- MenuTabs <- TabPanel <- Accordion <- SpeedDial
Currently the state object is being changed as I expected but the Accordion isn't automatically re-rendered. The user needs to refresh the screen to see the new item.
I don't understand why, since I believed that when the state of a parent component changed (the Form in my case) the children of this component would be re-rendered (the Accordion in my case).
The parent component (Form) has the following state:
const [dishCards, setDishCards] = React.useState(
[
{
tabLabel: 'General',
tabDishes: [
{
dishName: 'Dish 1',
dishPrice: '',
vegan: false,
vegetarian: false,
spicy: 0
}
]
}
]
);
The Accordion component renders the cards depending on the state:
return (
<div>
{keys.map(cardNum => {
const dishName = dishCards[currentTab].tabDishes[cardNum].dishName;
return (
<Accordion key={cardNum}>
<AccordionSummary
expandIcon={<ExpandMoreIcon />}
aria-controls="panel1a-content"
>
<Typography>{dishName}</Typography>
</AccordionSummary>
<AccordionDetails>
<DishCard formik={formik} currentTab={currentTab} num={cardNum} dishCards={dishCards} />
</AccordionDetails>
</Accordion>
)
})}
<Stack direction="row">
<EditSpeedDial formik={formik} currentTab={currentTab} open={editDialogOpen} setOpen={setEditDialogOpen} handleDialogClose={handleEditDialogClose} />
<AddSpeedDial formik={formik} currentTab={currentTab} open={createDialogOpen} setOpen={setCreateDialogOpen} handleDialogClose={handleCreateDialogClose} dishCards={dishCards} setDishCards={setDishCards}/>
</Stack>
</div>
);
While the SpeedDial component sets the state as such:
function handleNewDish() {
const dishesCount = dishCards[currentTab].tabDishes.length;
const newDish = {
dishName: `Dish ${dishesCount+1}`,
dishPrice: '',
vegan: false,
vegetarian: false,
spicy: 0
}
const newDishCards = dishCards;
newDishCards[currentTab].tabDishes.push(newDish);
setDishCards(
newDishCards
);
}
I saw a similar question and the answer had to do with not directly mutating the state but I believe I'm not doing that, since I'm assigning the previous state (dishCards
) to a new variable before mutating it.
(By the way, I know I could use Redux to better manage state but nevertheless I'd like to understand why the behavior is not as I expected.)
Upvotes: 0
Views: 57
Reputation: 1832
Inside handleNewDish
, try changing:
const newDishCards = dishCards;
to:
const newDishCards = [...dishCards];
By spreading the old array into a new one, you change the reference of newDishCards
and dishCards
, telling React to re-render when you call setDishCards
Upvotes: 1