Skip to content

Instantly share code, notes, and snippets.

@cladley
Created April 29, 2019 11:13
Show Gist options
  • Save cladley/a43cdb0b2226f630ac13767d5aa2dc49 to your computer and use it in GitHub Desktop.
Save cladley/a43cdb0b2226f630ac13767d5aa2dc49 to your computer and use it in GitHub Desktop.
A better accordion example hook
import React, {useState, useImperativeHandle, useRef, useEffect, useMemo, useCallback} from 'react';
const AccordionContext = React.createContext();
function AccordionProvider(props) {
const accordionItems = new Map();
const registerAccordionItem = (id, item) => {
accordionItems.set(id, item);
};
const closeOtherItems = (idNotToClose) => {
accordionItems.forEach((item, key) => {
if (key !== idNotToClose) {
item.close();
}
});
};
return <AccordionContext.Provider {...props} value={{registerAccordionItem,closeOtherItems }} />;
}
function useAccordionContext() {
const context = React.useContext(AccordionContext)
if (!context) {
throw new Error(`useAccordionContext must be used within a AccordionProvider`)
}
return context
}
export function useAccordionItem() {
const {registerAccordionItem, closeOtherItems} = useAccordionContext();
const [isOpen, setIsOpen] = useState(false);
const ref = useRef(null);
const id = useMemo(() => String(Math.random()), []);
useImperativeHandle(ref, () => ({
close: () => {
setIsOpen(false);
}
}));
useEffect(() => {
registerAccordionItem(id, ref.current);
}, []);
const toggle = useCallback(() => {
setIsOpen(o => !o);
closeOtherItems(id);
}, [id]);
return [isOpen, toggle];
}
export function AccordionItem({title, children}) {
const [isOpen, toggle] = useAccordionItem();
return (
<div className="accordion-item">
<h5 onClick={toggle}>{title}</h5>
{isOpen && <div className="accordion-content">{children}</div>}
</div>
);
}
export function Accordion(props) {
return (
<AccordionProvider>
<div className="accordion">
{props.children}
</div>
</AccordionProvider>
);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment