티스토리 뷰
// 개별 선택
export const onCheckedHandler = (
e: ChangeEvent<HTMLInputElement>,
item: any,
setCheckItems: React.Dispatch<React.SetStateAction<any[]>>
) => {
if (e.target.checked) {
setCheckItems(prev => [...prev, item]);
} else {
setCheckItems(prev => prev.filter(check => check.id !== item.id));
}
};
// 전체 선택
export const allCheckedHandler = (
e: ChangeEvent<HTMLInputElement>,
data: any[],
setCheckItems: React.Dispatch<React.SetStateAction<any[]>>
) => {
if (e.target.checked) {
setCheckItems(data);
} else {
setCheckItems([]);
}
};
const SearchedData = () => {
const [checkItems, setCheckItems] = useState<SearchTypes[]>([]);
return (
<div>
<div>
<input
type="checkbox"
onChange={e => allCheckedHandler(e, searchData, setCheckItems)}
checked={checkItems.length === searchData.length}
id="allChecked"
className={styles["all-input"]}
/>
<label htmlFor="allChecked">전체선택</label>
<select name="" id="">
<option value="">정확도순</option>
<option value="">평점순</option>
<option value="">후기순</option>
<option value="">가격순</option>
<option value="">판매량순</option>
<option value="">출시일순</option>
<option value="">상품명순</option>
</select>
{searchData?.map(item => {
return (
<div key={item.id}>
<input
type="checkbox"
onChange={e => onCheckedHandler(e, item, setCheckItems)}
checked={checkItems.some(check => check.id === item.id)}
/>
<div>{item.title}</div>
<div>{item.publisher}</div>
<div>{item.author}</div>
</div>
);
})}
</div>
</div>
);
};
개별선택
- input 타입은 체크박스로 만든다
- onCheck 이벤트가 아닌, onChange 이벤트를 사용한다
- onCheck은 클릭하는 순간 이벤트가 트리거 되기 때문에 상태가 변경되지 않아도 이벤트가 호출되지만 onChange는 상태 변화를 감지해 호출되기 때문
- checked 속성은 체크박스와 라디오 버튼의 선택 상태를 반영하고 boolen값을 반환한다
- 조건문으로 이벤트 대상 중(e.target) 체크된 데이터만 (checked: true) state 저장한다
- 개별선택의 경우, 한 번에 여러 개를 선택하는 게 아닌 하나씩 선택이기 때문에 이전값에 새로운 값을 추가해줘야 한다
- setCheckItems(prev => [...prev, item]);
- prev는 이전에 선택된 데이터 목록
- item은 현재 새로 선택한 데이터
- [...prev, item]은 기존 선택 목록에 새 데이터를 추가한 새로운 배열을 만들어서 setCheckItems에 저장
- 선택 해제의 경우 state에 저장된 데이터와 (checked: false) 데이터를 비교, id가 같지 않은 것만 반환하면 체크된 데이터만 남게 된다
- setCheckItems(prev => prev.filter(selectedItem => selectedItem.id !== item.id));
- prev는 현재 선택된 데이터 목록
- filter 메서드는 선택된 데이터 중 해제할 데이터와 id가 일치하지 않는 항목만 남겨 새로운 배열을 만들어서 setCheckItems에 저장
전체 선택
- 체크 상태를 확인하기 위해 input를 사용한다
- lable을 이용해 input을 감싸 전체선택 텍스트만 클릭해도 input이 작동하도록 연결해 준다
- 꼭 감싸지 않아도 label 태그에 htmlFor (for) 속성과 input id속성을 동일하게 해도 연결된다
- checked 속성을 이용해 이벤트 대상이(e.target) 체크 상태이면 state에 데이터를 저장한다
- 이벤트 대상이(e.target) 체크상태가 아닌 경우 state를 빈 배열로 만들어준다
*전체선택 시 버튼 대신 레이블을 사용한 이유
여러 페이지에서 사용하는 기능이라 공용 함수 파일에 분리해 재사용할 수 있도록 했다
전체선택 텍스트 옆에 체크박스가 노출되는 것과, 노출되지 않는 페이지가 있어서 체크박스가 보이지 않는 페이지는 display:none로 숨겨서 사용하면 되기 때문에 레이블로 통일했다
레이블로 인풋을 감싸지 않는 경우
<input
type="checkbox"
onChange={e => allCheckedHandler(e, searchData, setCheckItems)}
checked={checkItems.length === searchData.length}
id="allChecked"
className={styles["all-input"]}
/>
<label htmlFor="allChecked">전체선택</label>