🍊
[Output]Reactとstyled-componentsで簡易的なアコーディオンを作ってみた
ReactとCSSinJSの学習として、アコーディオンを簡易的に作ってみました。
結論
色んな入門動画を見ながら一通りReactを触っていましたが、実際に自分で一から作るとなると中々うまくはいきませんでした。
使用言語
- React
- TypeScript
- styled-components
実際のコード
Accordion.tsx
import { ReactNode, useLayoutEffect, useRef, useState } from "react";
import styled from "styled-components";
interface AccordionInterface {
heading: string;
children: ReactNode;
initialOpen: boolean;
}
const Div_Accordion = styled.div`
border: 1px solid #000;
padding: 10px;
border-radius: 8px;
`;
const Summary_AccordionHead = styled.summary`
cursor: pointer;
`;
const Div_AccordionContents = styled.div<{
$isOpen: boolean;
$maxHeight: number;
}>`
transition: max-height 0.5s ease;
overflow: hidden;
max-height: ${(props) => (props.$isOpen ? `${props.$maxHeight}px` : "0")};
`;
const Accordion = (props: AccordionInterface) => {
const contentsRef = useRef<HTMLDivElement>(null);
const [isOpen, setIsOpen] = useState(props.initialOpen);
const [maxHeight, setMaxHeight] = useState<number>(0);
useLayoutEffect(() => {
if (contentsRef.current) {
setMaxHeight(contentsRef.current.scrollHeight);
}
}, [isOpen]);
return (
<Div_Accordion>
<Summary_AccordionHead
onClick={() => {
setIsOpen(!isOpen);
}}
>
{props.heading}
</Summary_AccordionHead>
<Div_AccordionContents
$maxHeight={maxHeight}
$isOpen={isOpen}
ref={contentsRef}
>
<div>{props.children}</div>
</Div_AccordionContents>
</Div_Accordion>
);
};
export default Accordion;
やりのこしていること
- initialOpenをpropsでtrueを渡すと、はじめから開いている状態になりますが、アニメーションが付与されてページ高さがスクロール中や初期表示時に変わる可能性があります。これをinitialOpenがtrueのときはアニメーションなしで表示できるようにしたい。
参考記事
React(Next.js)で滑らかな開閉アニメーションとアクセシビリティを考慮したアコーディオンを実装する
Discussion