/* Dropdown.tsx */
import { useState, useEffect, useCallback, useRef } from 'react';
import Image from 'next/image';
/* props로 받아야하는 것: 옵션 리스트, */
const Dropdown = (props: any) => {
const { itemList, placeholder } = props;
/* 드랍다운 열린 상태/닫힌 상태 (true/false) */
const [isActive, setIsActive] = useState<boolean>(false);
const [item, setItem] = useState<HTMLElement[] | undefined>();
const selectedRef = useRef<HTMLDivElement>(null);
const onActiveToggle = useCallback(() => {
setIsActive((prev) => !prev);
}, []);
const onSelectItem = useCallback((e) => {
setItem(e.target.parentNode.childNodes);
setIsActive((prev) => !prev);
}, []);
const container = useRef<HTMLDivElement>(null);
return (
<div className="dropdown-container">
<div
className="select-container"
onClick={onActiveToggle}
ref={container}
>
{isActive ? (
<>
{item ? (
<div className="selected">선택된 값</div>
) : (
<div className="placeholder">{placeholder}</div>
)}
<div className="toggle-btn active" />
</>
) : (
<>
{item ? (
<div className="selected">선택된 값</div>
) : (
<div className="placeholder">{placeholder}</div>
)}
<div className="toggle-btn " />
</>
)}
</div>
{isActive ? (
/* 드랍다운 목록 태그 */
<div
className="option-container"
style={
container.current
? { width: container.current.clientWidth }
: { width: '100%' }
/* width는 부모 태그(container)의 현재 가시너비 값을 받는다. */
}
>
{Object.keys(itemList[0]).includes('img')
? itemList.map((el, index) => {
return (
<div
id="item"
className="item with-icon"
key={index}
onClick={onSelectItem}
>
<img src={el.img} width={18} height={18} />
<span id="item_name">{el.name}</span>
</div>
);
})
: itemList.map((el, index) => {
return (
<div
className="item"
key={index}
onClick={onSelectItem}
id="item"
>
<span id="item_name">{el.name}</span>
</div>
);
})}
</div>
) : null}
</div>
);
};
export default Dropdown;
➡️ useRef
를 사용해 부모 태그를 참조하고, 이 부모 태그의 가시 너비를 width 속성으로 받는다.
clientWidth
속성이란?padding을 포함한 px 단위의 요소 가시너비.
이 때 border
,scrollbar
, margin
은 제외한다.
참고자료
[MDN Web Docs]Element.clientWidth
[둠발롬님 - 기록은 진리다][JS] HTML 요소 크기가져오기. (offsetWidth, clientWidth, scrollWidth, getBoundingClientRect
/* pointlog.tsx*/
const PointLog = () => {
const [ActiveTab, setActiveTab] = useState('all');
return <>
// ...생략
<div className="tabmenu">
<div
className={ActiveTab === 'all' ? `item active` : 'item'}
onClick={(e) => setActiveTab('all')}
>
전체
</div>
<div
className={ActiveTab === 'plus' ? `item active` : 'item'}
onClick={(e) => setActiveTab('plus')}
>
적립
</div>
<div
className={ActiveTab === 'minus' ? `item active` : 'item'}
onClick={(e) => setActiveTab('minus')}
>
차감
</div>
</div></>
}
팀장님에게서 매우 심각한 삼항연산자 중독 증상을 진단받고, 함수를 실행함으로써 최대한 코드 재사용을 줄일 것을 권고받았다.
/* pointlog.tsx*/
const TabArray = [
{ eng: 'all', kor: '전체' },
{ eng: 'plus', kor: '지급' },
{ eng: 'minus', kor: '차감' },
];
const PointLog = () => {
const [ActiveTab, setActiveTab] = useState('all');
const createTabMenu = (array: { eng: string; kor: string }[]) => {
return array.map((el) => {
let className = 'item';
if (el.eng === ActiveTab) {
className = 'item active';
}
return (
<div
className={className}
onClick={(e) => {
setActiveTab(el.eng);
}}
>
{el.kor}
</div>
);
});
};
return (
<>
//...생략
<div className="tabmenu">{createTabMenu(TabArray)}</div>
</>)
기능은 똑같이 구현되면서, 코드량은 줄어들었다.
삼항연산자를 남용한 코드들은 하나하나씩 리팩토링을 해봐야겠다...