// 1. Select 컴포넌트 코드
export const Select = ({
placeholder = 'Please Select',
error = false,
disabled = false,
option = [],
val,
onChange,
}) => {
const [isShowOption, setShowOption] = useState(false);
const handleVal = (item) => {
onChange(item);
setShowOption(false);
};
return (
<div
className={cn(
'select__wrapper',
error && 'select--error',
disabled && 'select--disabled',
)}
>
<button
className={cn('select')}
type='button'
onClick={() => setShowOption((prev) => !prev)}
onMouseDown={(e) => e.preventDefault()}
>
{val.label || placeholder}
<ArrowDropDownIcon className='select__icon' />
</button>
{isShowOption && (
<ul className='select__list'>
{option.map((item) => (
<li
className={cn('select__list--item', val.value === item.value && 'selected')}
onClick={() => handleVal(item)}
role='presentation'
key={item.value}
>
{item.label}
</li>
))}
</ul>
)}
</div>
);
};
Select.propTypes = {
placeholder: PropTypes.string,
error: PropTypes.bool,
disabled: PropTypes.bool,
option: PropTypes.arrayOf(
PropTypes.shape({
value: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
label: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
}),
),
val: PropTypes.shape({
value: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
label: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
}),
onChange: PropTypes.func,
};
// 2. select 컴포넌트를 불러온 페이지 코드
const list = [
{ value: 'opt1', label: 'opt1' },
{ value: 'opt2', label: 'opt2' },
];
const [selectedVal, setSelectedVal] = useState({ value: 'opt1', label: 'opt1' });
// return 영역
<Select
placeholder='선택하세요.'
option={list}
val={selectedVal}
onChange={(newVal) => setSelectedVal(newVal)}
/>
<select>, <option>태그로는 디자인 커스텀에 제한이 있어서
<button>과 <ul>로 select를 만들었다.
문제는 option역할을 하는 li를 클릭해도
내가 원하는 값으로 select값이 바뀌어 보이지가 않는 것이다.
그 이유는 맨 처음엔 드롭다운을 button에 갔던 포커스가 onBlur가 되면 사라지도록 코드를 작성했었는데..
onBlur로 인해 onClick이 되기도 전에 드롭다운이 닫히면서 값이 안바뀌는 거였다.
그러나 handleBar라는 함수를 이용해서
드롭다운이 꺼지는 코드를 값이 바뀌고 난뒤에 실행시키면
내가 원하는 select를 만들 수 있다.. 감동
그리고 이 코드는 오직 셀렉트 버튼과 옵션을 누를 때만 option이 사라지지만
셀렉트 버튼을 제외한 영역을 누르면 꺼지도록 만들수도 있다.
ref로 select를 설정하고
mousedown이벤트가 있을 때마다 document.addEventListener('mousedown',~
selectRef가 event.target(클릭한 영역)에 포함되어 있는지 조건문으로 걸러서
해당되지 않으면 option리스트가 꺼지도록 하면 된다.
import React, { useState, useEffect, useRef } from 'react';
// 1. Select 컴포넌트 코드
export const Select = ({
placeholder = 'Please Select',
error = false,
disabled = false,
option = [],
val,
onChange,
}) => {
const [isShowOption, setShowOption] = useState(false);
const selectRef = useRef(null);
useEffect(() => {
const handleClickOutside = (event) => {
if (selectRef.current && !selectRef.current.contains(event.target)) {
setShowOption(false);
}
};
document.addEventListener('mousedown', handleClickOutside);
return () => {
document.removeEventListener('mousedown', handleClickOutside);
};
}, []);
const handleVal = (item) => {
onChange(item);
setShowOption(false);
};
return (
<div
ref={selectRef}
className={cn(
'select__wrapper',
error && 'select--error',
disabled && 'select--disabled',
)}
>
<button
className={cn('select')}
type='button'
onClick={() => setShowOption((prev) => !prev)}
onMouseDown={(e) => e.preventDefault()}
>
{val.label || placeholder}
<ArrowDropDownIcon className='select__icon' />
</button>
{isShowOption && (
<ul className='select__list'>
{option.map((item) => (
<li
className={cn('select__list--item', val.value === item.value && 'selected')}
onClick={() => handleVal(item)}
role='presentation'
key={item.value}
>
{item.label}
</li>
))}
</ul>
)}
</div>
);
};
Select.propTypes = {
placeholder: PropTypes.string,
error: PropTypes.bool,
disabled: PropTypes.bool,
option: PropTypes.arrayOf(
PropTypes.shape({
value: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
label: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
}),
),
val: PropTypes.shape({
value: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
label: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
}),
onChange: PropTypes.func,
};
'JavaScript > React' 카테고리의 다른 글
[React Native] react-native-shadow-2로 shadow 효과 주기 (0) | 2024.02.26 |
---|