Skip to content

Instantly share code, notes, and snippets.

@lahin31
Last active September 23, 2022 19:37
Show Gist options
  • Save lahin31/b1e0d117efc52a26ab99683427b45ba6 to your computer and use it in GitHub Desktop.
Save lahin31/b1e0d117efc52a26ab99683427b45ba6 to your computer and use it in GitHub Desktop.
// accordion component using Compound Component (with context)
// app.js
function App() {
return (
<div className="App">
<Accordion defaultSelect="1" collapsible>
<AccordionItem id="1">
<AccordionToggle>Section 1</AccordionToggle>
<AccordionPanel>Section 1 content</AccordionPanel>
</AccordionItem>
<AccordionItem id="2">
<AccordionToggle>Section 2</AccordionToggle>
<AccordionPanel>Section 2 content</AccordionPanel>
</AccordionItem>
</Accordion>
</div>
);
}
// accordion.js
import React, { useState } from "react";
import {
AccordionContext,
AccordionItemContext,
useAccordion,
useAccordionItem,
} from "./AccordionContext";
const Accordion = ({ defaultSelect, collapsible, children }) => {
const [activePanel, setActivePanel] = useState(defaultSelect);
const accordionContextValue = {
activePanel,
handlePanelClick: (id) => {
let nextItemId = id;
if (collapsible && nextItemId === activePanel) {
nextItemId = null;
}
setActivePanel(nextItemId);
},
};
return (
<AccordionContext.Provider value={accordionContextValue}>
{children}
</AccordionContext.Provider>
);
};
export const AccordionItem = ({ id, children }) => {
const contextValue = { id };
return (
<AccordionItemContext.Provider value={contextValue}>
{children}
</AccordionItemContext.Provider>
);
};
export const AccordionToggle = ({ children }) => {
const { handlePanelClick } = useAccordion();
const { id } = useAccordionItem();
return (
<button
style={{
width: "100%",
height: "30px",
border: "1px solid #ebebeb",
textAlign: "left",
padding: "0px 20px",
}}
onClick={() => handlePanelClick(id)}
>
{children}
</button>
);
};
export const AccordionPanel = ({ children }) => {
const { id } = useAccordionItem();
const { activePanel } = useAccordion();
if (id !== activePanel) return null;
return (
<div
style={{
border: "1px solid #ebebeb",
textAlign: "left",
padding: "20px",
}}
>
{children}
</div>
);
};
export default Accordion;
// accordion-context.js
import React, { useContext } from "react";
export const AccordionContext = React.createContext(null);
export const AccordionItemContext = React.createContext(null);
export function useAccordion() {
const context = useContext(AccordionContext);
if (context === undefined) {
throw new Error("useAccordion must be used within a <Accordion />");
}
return context;
}
export function useAccordionItem() {
const context = useContext(AccordionItemContext);
if (context === undefined) {
throw new Error("useAccordion must be used within a <Accordion />");
}
return context;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment