본문 바로가기
TIL

react/typescript input checkbox 전체 선택 구현

by dev__log 2024. 3. 29.

그동안 프로젝트 진행하면서 구현한 적이 없어서 이번에 연습 삼아 구현하게 되었다. 

 

기능

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;