'Refactoring tabs according to react best practices
Here is a FAQ component with a child Accordion. As identifier I use anchor that I get from data, written for every element as tab1, tab2, tab3 etc. (instead of index, because indexes are duplicated when I need render accordion twice or more times).
Accordion requirements is: first element open when page is loaded, previous element closes when next element opens.
My problem is using checking class instead of using state for switching tabs. Please, help me to do it right way according to react rules.
Parent
export const FAQBlock = ({ data }) => {
const [open, setOpen] = useState(false);
return (
<StyledFAQBlock>
<div className="content">
{data.items &&
data.items.map((item, index) => (
<Accordion
key={index}
index={index}
variant="columns"
title={item.title}
text={item.text}
anchor={item.anchor}
faqState={{ open: open, setOpen: setOpen }}
/>
))}
</div>
</StyledFAQBlock>
);
};
Child
export const Accordion = props => {
const { title, text, variant, anchor, faqState } = props;
const [open, setOpen] = useState(false);
const bodyRef = useRef(null);
const contentRef = useRef(null);
useEffect(() => {
if (faqState.open === anchor) {
openAccordion();
} else {
closeAccordion();
}
}, [faqState.open]);
useEffect(() => {
anchor == "tab1" && openAccordion();
}, []);
const getContentHeight = () => {
return contentRef.current.offsetHeight;
};
const openAccordion = () => {
setOpen(true);
bodyRef.current.style.height = `${getContentHeight()}px`;
};
const closeAccordion = () => {
setOpen(false);
bodyRef.current.style.height = "0";
};
const handleClick = () => {
if (faqState.open === anchor) {
if (open) {
closeAccordion();
} else {
openAccordion();
}
} else {
faqState.setOpen(anchor);
}
};
const handleKeyPress = event => {
if (event.charCode === 13) {
handleClick();
}
};
return (
<StyledAccordion className={anchor} variant={variant} open={open}>
<div
className="panel"
onClick={handleClick}
onKeyPress={handleKeyPress}
tabIndex="0"
role="button"
aria-pressed={open}
>
<h3 className="title" dangerouslySetInnerHTML={{ __html: title }} />
</div>
<div className="text" ref={bodyRef}>
<p ref={contentRef} dangerouslySetInnerHTML={{ __html: text }}></p>
</div>
</StyledAccordion>
);
};
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|
