I have two lists of accordions. I want both accordion sets to be in sync with each other. For example, if I open accordion 1 in the left, I want accordion 1 in the right to be open and vice versa. I am already maintaining the state to keep the accordions open by default Please help me on what I am doing wrong.
This is my code:
import * as React from 'react';
import Accordion from '@mui/material/Accordion';
import AccordionDetails from '@mui/material/AccordionDetails';
import AccordionSummary from '@mui/material/AccordionSummary';
import Typography from '@mui/material/Typography';
import ExpandMoreIcon from '@mui/icons-material/ExpandMore';
export default function ControlledAccordions() {
const [expanded, setExpanded] = React.useState<Record<string, boolean>>({});
const arr = [1, 2, 3];
React.useEffect(() => {
setExpanded(
arr.reduce((a, c) => {
a[`panel${c}`] = true;
return a;
}, {} as Record<string, boolean>)
);
}, []);
const handleChange =
(panel: string) => (event: React.SyntheticEvent, isExpanded: boolean) => {
setExpanded({
...expanded,
[panel]: isExpanded,
});
};
console.log('expanded', expanded);
return (
<div
style={{
display: 'flex',
flexDirection: 'row',
}}
>
<div>
{arr.map((i) => (
<Accordion
expanded={expanded[`panel${i}`]}
onChange={handleChange(`panel${i}`)}
>
<AccordionSummary
expandIcon={<ExpandMoreIcon />}
aria-controls="panel1bh-content"
id={`leftpanel${i}bh-header`}
>
<Typography sx={{ width: '33%', flexShrink: 0 }}>
General settings
</Typography>
<Typography sx={{ color: 'text.secondary' }}>
I am an accordion
</Typography>
</AccordionSummary>
<AccordionDetails>
<Typography>
Nulla facilisi. Phasellus sollicitudin nulla et quam mattis
feugiat. Aliquam eget maximus est, id dignissim quam.
</Typography>
</AccordionDetails>
</Accordion>
))}
</div>
<div>
{arr.map((i) => (
<Accordion
expanded={expanded[`panel${i}`]}
onChange={handleChange(`panel${i}`)}
>
<AccordionSummary
expandIcon={<ExpandMoreIcon />}
aria-controls="panel1bh-content"
id={`rightpanel${i}bh-header`}
>
<Typography sx={{ width: '33%', flexShrink: 0 }}>
General settings
</Typography>
<Typography sx={{ color: 'text.secondary' }}>
I am an accordion
</Typography>
</AccordionSummary>
<AccordionDetails>
<Typography>
Nulla facilisi. Phasellus sollicitudin nulla et quam mattis
feugiat. Aliquam eget maximus est, id dignissim quam.
</Typography>
</AccordionDetails>
</Accordion>
))}
</div>
</div>
);
}
My Stackblitz URL: https://stackblitz.com/edit/react-7qktzd?file=Demo.tsx
Please advice.
2
Answers
I was able to fix it by checking if its empty and render the accordion only if the state value is not empty.
https://stackblitz.com/edit/react-7qktzd-imzy6p?file=Demo.tsx
I think this can be fixed if you remove the first
useEffect
since you are just trying to set the initial state of the accordions as opened? Andarr
doesn’t change after that anyways.Here’s how I would go about it:
arr
to set the initial state ofexpanded
:Accordion
expanded attribute to whatever is inexpanded
state, which will be guaranteed to have a value by the time it gets here:And all together:
But you should still consider moving things to a parent component like the react docs says here: https://react.dev/learn/sharing-state-between-components#controlled-and-uncontrolled-components