Sematic UI에 따르면 사용자는 아코디언을 사용하여 컨텐츠 섹션 표시를 토글 할 수 있습니다.
이 포스트에서는 재사용 성이 높은 아코디언 구성 요소를 처음부터 작성합니다.
우리는 React와 그 후크 API를 사용할 것입니다.
https://smellycode.com/accordion-in-reactjs/
샘플 아코디언 (코드 및 상자)
아코디언 구성 요소는 다음 구성 요소로 분류 할 수 있습니다.
루트 구성 요소의 이름을 <Accordion />으로 지정하겠습니다. 아코디언은 자녀에게 필요한 데이터를 제공 할 책임이 있습니다.
렌더링 props 또는 복합 구성 요소를 사용하여 구현할 수 있습니다. 복합 구성 요소 패턴을 사용하여 아코디언을 쉽게 추론 할 수 있습니다.
또한 복합 구성 요소 패턴으로 인해 JSX는 의미상 더 의미 있고 아름답습니다.
샘플 아코디언 JSX.
<Accordion>
<Accordion.Toggle>Click Me</Accordion.Toggle>
<Accordion.Collapse>Some collapsable text</Accordion.Collapse>
</Accordion>
각 아코디언 구성 요소를 디자인 해 보겠습니다.
<Accordion />
아코디언 구성 요소는 컨테이너 역할을합니다.
요소 소품은 컨테이너 요소 / 구성 요소로 사용됩니다.
요소의 기본값은 div로 설정되어 있습니다.
아코디언에는 onToggle, activeEventKey 등의 다른 소품도 있습니다.
참고 : PropTypes 패키지는 유형 확인에 사용됩니다.
const Accordion = ({
element: Component,
activeEventKey,
onToggle,
children,
...otherProps
}) => {
return <Component {...otherProps}>{children}</Component>;
};
Accordion.propTypes = {
// Element or Component to be rendered as a parent for accordion.
element: PropTypes.oneOfType([PropTypes.string, PropTypes.func]),
// `eventKey` of the accordion/section which is active/open
activeEventKey: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
// onToggle callback. (eventKey) => void
onToggle: PropTypes.func
};
Accordion.defaultProps = {
// default render as div
element: 'div'
};
<Accordion.Toggle />
이름에서 알 수 있듯이 Accordion.Toggle은 내용을 토글합니다.
또한 Accordion 구성 요소와 같은 요소 소품도 받습니다.
Accordion.Collapse 구성 요소에 매핑 된 eventKey 소품이 필요합니다.
const Toggle = ({
element: Component,
eventKey,
onClick,
children,
...otherProps
}) => {
return <Component {...otherProps}>{children}</Component>;
};
Toggle.propTypes = {
// Element or Component to be rendered as a toggle.
element: PropTypes.oneOfType([PropTypes.string, PropTypes.func]),
// `eventKey` of the content to be controlled.
eventKey: PropTypes.oneOfType([PropTypes.string, PropTypes.number])
};
Toggle.defaultProps = {
element: 'div'
};
<Accordion.Collapse />
Accordion.Collapse 구성 요소는 콘텐츠를 조건부로 렌더링 합니다. 다른 구성 요소와 마찬가지로 요소 소품도 받습니다.
const Collapse = ({
element: Component,
eventKey,
children,
...otherProps
}) => {
return <Component {...otherProps}>{children}</Component>;
};
Collapse.propTypes = {
// Wrapper for target content.
element: PropTypes.oneOfType([PropTypes.string, PropTypes.func]),
// Event key for the content.
eventKey: PropTypes.oneOfType([PropTypes.string, PropTypes.number])
};
Collapse.defaultProps = {
element: 'div'
};
Toggle과 Collapse with Accordion 네임 스페이스를 모두 내보낼 것입니다.
// ...
// Accordion's code
// ...
Accordion.Toggle = Toggle;Accordion.Collapse = Collapse;
우리는 기초를 마련했습니다. 다른 개념을 소개하겠습니다.
useAccordionContext
각 아코디언 구성 요소에는 아코디언 구성 요소의 일부 데이터가 필요합니다.
예. Accordion.Collapse는 컨텐츠를 조건부로 표시하려면 activeEventKey를 알아야 합니다.
Accordion.Toggle은 적절한 매개 변수로 전환 콜백을 호출하기 위해 activeEventKey를 알아야 합니다.
각 아코디언 구성 요소에 소품으로 데이터를 수동으로 전달할 수 있습니다.
그러나 아코디언 컴포넌트 트리에서의 위치에 대한 지식이 필요합니다.
React는 이러한 사용 사례에 대한 더 나은 대안을 제공합니다.
React Context. 컨텍스트 API를 사용하면 모든 수준에서 소품을 수동으로 전달하지 않고도 구성 요소 트리를 통해 데이터를 전달할 수 있습니다.
아코디언을 위한 컨텍스트를 만들어 봅시다.
AccordionContext.js
import React from 'react';
export default React.createContext(null);
아코디언 컨텍스트에 액세스하려면 후크하십시오.
import { useContext } from 'react';
import AccordionContext from '../AccordionContext';
const useAccordionContext = () => {
const context = useContext(AccordionContext);
if (!context) {
throw new Error(
'Accordion components are compound component. Must be used inside Accordion.'
);
}
return context;
};
export default useAccordionContext;
Accordion은 activeEventKey 속성과 onToggle 메소드를 사용하여 AccordionContext를 초기화합니다.
const Accordion = ({
element: Component,
activeEventKey,
onToggle,
children,
...otherProps
}) => {
const context = useMemo(() => { return { activeEventKey, onToggle }; }, [activeEventKey, onToggle]);
return ( <AccordionContext.Provider value={context}> <Component {...otherProps}>{children}</Component>
</AccordionContext.Provider> );
};
Accordion.Collapse는 컨텍스트에서 activeEventKey를 읽습니다. activeEventKey가 eventKey와 같은 경우 컨텐츠를 렌더링 합니다.
import { useAccordionContext } from '../hooks';
const Collapse = ({
element: Component,
eventKey,
children,
...otherProps
}) => {
const { activeEventKey } = useAccordionContext(); return activeEventKey === eventKey ? ( <Component {...otherProps}>{children}</Component>
) : null;};
Accordion.Toggle은 토글 컴포넌트 / 요소를 클릭 할 때 컨텍스트에서 onToggle 함수를 호출합니다.
import { useAccordionContext } from '../hooks';
const useAccordionClick = (eventKey, onClick) => { const { onToggle, activeEventKey } = useAccordionContext(); return event => { onToggle(eventKey === activeEventKey ? null : eventKey); if (onClick) { onClick(event); } };};
const Toggle = ({
element: Component,
eventKey,
onClick,
children,
...otherProps
}) => {
const accordionClick = useAccordionClick(eventKey, onClick); return ( <Component onClick={accordionClick} {...otherProps}> {children} </Component> );};
카드 구성 요소를 사용하여 App의 모든 것을 연결 한 후 :
import Accordion from './Accordion';
import Card from './Card';
const content = [
// items in {question, answer} format
// ...
// ...
];
export default function App() {
const [activeEventKey, setActiveEventKey] = useState(0);
return (
<div className="App">
<Accordion activeEventKey={activeEventKey} onToggle={setActiveEventKey}>
{content.map(({ question, answer }, index) => (
<Card key={index}>
<Accordion.Toggle element={Card.Header} eventKey={index}>
{question}
{activeEventKey !== index && <span>??</span>}
{activeEventKey === index && <span>??</span>}
</Accordion.Toggle>
<Accordion.Collapse eventKey={index} element={Card.Body}>
{answer}
</Accordion.Collapse>
</Card>
))}
</Accordion>
</div>
);
}
제어 가능성
아코디언 구성 요소는 제어되는 구성 요소입니다. 주가 없습니다. 소품으로 필요한 데이터를 수신합니다 (예 : 활성 섹션 키 activeEventKey, 토글 핸들러 onToggle 등).
Accordion의 제어 된 특성으로 인해 매우 유연하지만 단점이 있습니다.
소비자 구성 요소는 필요하지 않은 경우에도 아코디언 상태를 명시 적으로 관리해야 합니다.
명시적 상태 관리는 때때로 지루할 수 있으며 상용구를 유발할 수 있습니다.
아코디언에는 제어되지 않은 동작이 추가됩니다. 실제 앱에서는 제어 할 수 없는 것을 사용할 수 있습니다.
아코디언을 제어 불가능 / 제어 가능하게 하기 위해 아코디언에 로컬 상태를 소개합니다.
activeEventKey prop과 동기화 상태를 유지합니다.
깜박임을 피하기 위해 동기화에 useEffect 대신 useLayoutEffect를 사용합니다 (useEffect와 useLayoutEffect).
로컬 상태의 향상된 아코디언 구성 요소
const useEventKey = (eventKey, onToggle) => { const [activeEventKey, setActiveEventKey] = useState(eventKey); useLayoutEffect(() => { setActiveEventKey(eventKey); }, [eventKey, onToggle]); return [activeEventKey, setActiveEventKey];};
const Accordion = ({
element: Component,
activeEventKey,
onToggle,
children,
...otherProps
}) => {
const [eventKey, setEventKey] = useEventKey(activeEventKey, onToggle); const handleToggle = useCallback( eventKey => { if (activeEventKey !== undefined) { onToggle(eventKey); return; } setEventKey(eventKey); }, [activeEventKey, onToggle, setEventKey] );
const context = useMemo(() => {
return { activeEventKey: eventKey, onToggle: handleToggle }; }, [eventKey, handleToggle]);
return (
<AccordionContext.Provider value={context}>
<Component {...otherProps}>{children}</Component>
</AccordionContext.Provider>
);
};
Accordion.defaultProps = {
// default render as div
element: 'div',
onToggle: () => {}};
마지막으로, 아코디언의 제어 및 제어되지 않은 형태를 모두 갖춘 앱 구성 요소.
export default function App() {
const [activeEventKey, setActiveEventKey] = useState(0);
return (
<div className="App">
<h3>Uncontrolled Accordion</h3> <Accordion> {content.map(({ question, answer }, index) => ( <Card key={index}> <Accordion.Toggle element={Card.Header} eventKey={index}> {index + 1}. {question} </Accordion.Toggle> <Accordion.Collapse eventKey={index} element={Card.Body}> {answer} </Accordion.Collapse> </Card> ))} </Accordion> <h3>Controlled Accordion</h3> <Accordion activeEventKey={activeEventKey} onToggle={setActiveEventKey}>
{content.map(({ question, answer }, index) => (
<Card key={index}>
<Accordion.Toggle element={Card.Header} eventKey={index}>
{index + 1}. {question}
{activeEventKey !== index && <span>??</span>}
{activeEventKey === index && <span>??</span>}
</Accordion.Toggle>
<Accordion.Collapse eventKey={index} element={Card.Body}>
{answer}
</Accordion.Collapse>
</Card>
))}
</Accordion>
</div>
);
}
등록된 댓글이 없습니다.