그동안 프로젝트 진행하면서 구현한 적이 없어서 이번에 연습 삼아 구현하게 되었다.
기능
1. 전체 선택 클릭 시 다른 체크박스 전체 선택/해제
2. 전체 선택 상태에서 다른 체크박스 해제 시 전체 선택 부분도 선택 해제
3. 체크박스 재사용 가능하도록 컴포넌트로 분리
Checkbox 컴포넌트 / Checkbox.tsx
재사용 가능하도록 props를 내려줬다.
import React from "react";
const Checkbox = ({ title, name, checked, onChange }: { title: string; name: string; checked: boolean; onChange: React.ChangeEventHandler<HTMLInputElement> }) => {
return (
<label>
<input type="checkbox" name={name} checked={checked} onChange={onChange} />
{title}
</label>
);
};
export default Checkbox;
가짜 데이터
const data = [
{ id: 0, title: "오리", name: "animal" },
{ id: 1, title: "호랑이", name: "animal" },
{ id: 2, title: "거위", name: "animal" },
{ id: 3, title: "사자", name: "animal" }
];
선택한 체크박스의 id 배열을 담은 useState
선택/해제 시 state가 바뀌면서 리렌더링 된다.
const [checkedItemList, setCheckedItemList] = useState<number[]>([]);
체크박스 전체 선택
선택 상태(true) 일 경우 data(가짜 데이터)에서 id 값만 가져와서 state를 변경한다.
해제 상태(false) 일 경우 state를 초기화한다.
const onChangeCheckedAllHandler = (e: React.ChangeEvent<HTMLInputElement>) => {
if (e.target.checked) {
const target = data.map((item) => item.id);
setCheckedItemList([...target]);
} else {
setCheckedItemList([]);
}
};
체크박스 단일 선택
const onChangeCheckedHandler = (e: React.ChangeEvent<HTMLInputElement>, id: number) => {
if (e.target.checked) {
setCheckedItemList((prev) => [...prev, id]);
} else {
setCheckedItemList(checkedItemList.filter((el) => el !== id));
}
};
렌더링 되는 부분
동물 전체 선택 input의 checkd 조건을 데이터 전체 수와 체크된 수가 같을 경우 체크가 되도록 했다.
오리, 호랑이 등 다른 요소를 하나라도 해제할 경우 조건이 맞지 않아 동물 전체 선택 input은 해제된다.
개별 동물 체크박스는 checkedItemList에 해당 id가 있는지 includes를 사용해서 체크 여부를 판단한다.
key 값은 절대 저렇게 넣으면 안 되지만 연습이라 대충 넣었다..
useEffect(() => {
console.log(checkedItemList);
}, [checkedItemList]);
console.log("렌더링이 다시 됐어요");
return (
<div>
<label>
<input type="checkbox" name="animal-all" checked={data.length === checkedItemList.length ? true : false} onChange={(e) => onChangeCheckedAllHandler(e)} /> 동물 선택
</label>
{data.map((item) => {
return (
<div key={item.name + item.id}>
<Checkbox title={item.title} name={item.name} checked={checkedItemList.includes(item.id)} onChange={(e) => onChangeCheckedHandler(e, item.id)} />
</div>
);
})}
</div>
);
name이 여러 종류가 있을 때 해당하는 name의 input을 선택/해제되도록 만들어도 좋을 것 같다.
전체 코드
/src/components/checkboxContents.tsx
"use client";
import React, { useEffect, useState } from "react";
import Checkbox from "./Checkbox";
const CheckboxContents = () => {
const [checkedItemList, setCheckedItemList] = useState<number[]>([]);
const data = [
{ id: 0, title: "오리", name: "animal" },
{ id: 1, title: "호랑이", name: "animal" },
{ id: 2, title: "거위", name: "animal" },
{ id: 3, title: "사자", name: "animal" }
];
const onChangeCheckedHandler = (e: React.ChangeEvent<HTMLInputElement>, id: number) => {
if (e.target.checked) {
setCheckedItemList((prev) => [...prev, id]);
} else {
setCheckedItemList(checkedItemList.filter((el) => el !== id));
}
};
const onChangeCheckedAllHandler = (e: React.ChangeEvent<HTMLInputElement>) => {
if (e.target.checked) {
const target = data.map((item) => item.id);
setCheckedItemList([...target]);
} else {
setCheckedItemList([]);
}
};
//테스트
useEffect(() => {
console.log(checkedItemList);
}, [checkedItemList]);
//테스트
console.log("렌더링이 다시 됐어요");
return (
<div>
<label>
<input type="checkbox" name="animal-all" checked={data.length === checkedItemList.length ? true : false} onChange={(e) => onChangeCheckedAllHandler(e)} /> 동물 선택
</label>
{data.map((item) => {
return (
<div key={item.name + item.id}>
<Checkbox title={item.title} name={item.name} checked={checkedItemList.includes(item.id)} onChange={(e) => onChangeCheckedHandler(e, item.id)} />
</div>
);
})}
</div>
);
};
export default CheckboxContents;
'TIL' 카테고리의 다른 글
상위 컴포넌트에서 하위 컴포넌트로 늦게 내려오는 문제 (0) | 2024.04.18 |
---|---|
[react/nextjs] 데이터가 있는지 판단 후 insert 또는 update (0) | 2024.04.05 |
[tailwind css] 반응형 및 color 셋팅 (0) | 2024.03.29 |
[프로그래머스] 모의고사 (0) | 2024.03.28 |
[supabase] insert, delete 북마크 추가/삭제 (0) | 2024.03.25 |