리액트로 state를 활용하여 체크박스 상태관리 하기

미연·2022년 6월 1일
0
post-thumbnail

부모 컴포넌트의 체크박스의 종류는 총 3가지가 있다.

  • 체크됨 : 모든 항목을 체크함
  • - : 체크한 항목이 한 개라도 있을 경우. 이 표시를 한 번 클릭했을 때, 체크한 항목이 모두 체크 해제 되어야 함.
  • 체크되지 않음 : 체크한 항목이 아예 없을 경우

부모 컴포넌트의 체크박스

1. state 선언

const [isCheckAll, setIsCheckAll] = useState(false)
const [isCheckingBox, setIsCheckingBox] = useState(false)
const [checkedArr, setCheckedArr] = useState([])
  • isCheckAll : 모든 항목을 체크했을 경우의 state
  • isCheckingBox : 체크한 항목이 한 개라도 있을 경우의 state
  • checkedArr : 체크한 항목의 정보를 객체로 저장시켜주는 state

2. 체크박스 구성하기

const changeAllCheck = (e) => {
    if (e.target.checked) {
      setIsCheckAll(true)
    } else {
      setIsCheckAll(false)
      setCheckedArr([])
    }
  }

  const checkingCheckedBox = () => {
    setIsCheckAll(false)
    setIsCheckingBox(true)
    setCheckedArr([])
  }

<div className='class_check_box'>
     <input type='checkbox' id='all_class_checkbox' onClick={e=> changeAllCheck(e)} checked={isCheckAll} />
     <label htmlFor='all_class_checkbox' />
         {checkedArr.length > 0 && checkedArr.length !== classData.length && (
                  <img src={checkingCheckbox} onClick={checkingCheckedBox} alt='checkingCheckbox' />
          )}
</div>
  1. 체크박스 input의 checked 속성은 위에서 선언한 isCheckAll state로 관리한다
  2. 체크박스를 클릭했을 경우, changeAllCheck 온클릭 이벤트가 활성화된다. 체크 여부에 따라 isCheckAll 속성도 바뀐다.
  3. 체크 해제 시, 빈배열으로 초기화되는 setCheckedArr([]) . 이 state는 체크한 항목의 정보를 객체로 담는 배열 state인데, 모든 항목 체크 해제시 배열을 초기화해주기 위해 작성하였다.
  4. - 상태는 Img 태그로 구성하였다.
  • 체크한 항목이 한 개라도 있을 경우와(checkedArr.length > 0) 체크한 항목 개수가 전체 데이터 개수가 같지 않을 때 (checkedArr.length !== classData.length)만 노출되도록 설정하였다.
  • 이를 클릭했을 때 checkingCheckedbox 온클릭 이벤트가 활성화된다.
    • 체크되었던 모든 항목이 체크 해제되고(setIsCheckAll(false)),
    • 현재 체킹중이라는 state가 켜지며(setIsCheckingBox(true)),
    • 모든 항목 체크가 해제되었으므로 선택된 항목 저장 배열도 초기화 된다.(setCheckedArr([]))

3. useEffect로 모든 체크박스 관리하기

  useEffect(() => {
    if (checkedArr.length !== 0 && checkedArr.length === classData.length) {
      setIsCheckAll(true)
    }
  }, [checkedArr])

체크한 항목이 하나라도 있고, 체크한 항목 개수가 전체 데이터 개수가 같을 때 모든 항목이 체크된다.

(= 개별 체크로 모든 항목을 다 체크했을 때, 모든 항목 체크가 켜져야 한다!)

4. 체크박스를 CSS로 커스텀하기

// 체크박스를 이미지로 커스텀 할 경우

input[type='checkbox'] {
  
}

input[type='checkbox'] + label {
   background: center/cover url('~') !important;
}

input[type='checkbox']:checked + label {
   background: center/cover url('~') !important;
}

5. 체크된 항목을 객체로 저장하기

 // 체크박스 전체선택을 했을 경우, 모든 강의 데이터를 담는 배열
  let allCheckedList = []

  const pushItemToBoxDataArr = (obj, status) => {
    let copyArray = [...checkedArr]

    // 자식 컴포에서 체크박스를 부분 체크했을 경우
    if (status === 'add') {
      copyArray.push(obj)
      copyArray.forEach((obj, idx) => (obj.index = idx + 1)) // order 키로 넘버링
      setCheckedArr(copyArray)
    } else if (status === 'delete') {
      // 자식 컴포에서 체크박스를 해제했을 경우, 강의명으로 비교하여 해당 원소 삭제
      const deleteArray = copyArray.filter((item) => item.index !== obj.index)
      setCheckedArr(deleteArray)
    } else if (status === 'all') {
      // 전체 선택박스를 체크했을 경우, 전역변수 빈 배열에 전체 데이터 담기
      allCheckedList.push(obj)
      allCheckedList.forEach((obj, idx) => (obj.order = idx + 1)) // order 키로 넘버링
      setCheckedArr(allCheckedList)
    }
  }
  • 이 메서드에서 체크된 항목의 객체들이 checkedArr state 배열에 저장된다. 이 메서드를 자식 컴포넌트에서 호출할 것이므로 Props로 내려주어야 한다.
  • 파라미터 구성
    • obj : 체크된 항목의 객체 정보
    • status : 어떤 상황(체크했을 때 / 체크 해제했을 때 / 모든 항목을 체크했을 때)일 때 이 메서드를 호출했는지에 대한 정보

1. status === 'add' : 체크했을 때

  • 임시로 만들어진 배열(copyArray)에 체크돼서 넘어온 객체를 저장(push)한다.
  • 나의 경우, 결과로 나오는 배열에서 numbering(1, 2, 3, ... 10)을 다시 해주어야 했으므로 forEach 메서드를 썼다.
  • 그리고 체크된 항목을 저장하는 배열에 넣었다. (setCheckedArr(copyArray))

2.status === 'delete' : 체크 해제했을 때

  • filter 메서드로 인덱스를 비교하여 해당 임시 배열에서 삭제시켰다.
  • 그리고 checkedArr에 덮어쓰기를 해주었다.

3. status === 'all' : 모든 항목을 체크했을 때

  • 모든 항목을 체크했을 경우, 모든 항목의 정보를 담아야 한다.
  • 이때 임시 복사한 배열이 아닌, 빈 배열 변수에 복사하여 덮어쓰기 해주었다.

map으로 돌린 자식 컴포넌트의 체크박스

1. state 선언

const [isCheck, setIsCheck] = useState(false)

자식 컴포넌트에서 각 항목들을 개별적으로 체크하는 상태관리 state를 선언한다.

2. 체크박스 구성하기

  const switchCheckedClass = (e) => {
    if (e.target.checked) {
      pushItemToBoxDataArr({
      'index': index,
      '강의 명': data.name,
      '강사 명': data.instructor_nickname,
      'URL 접근 권한': data.is_public ? '전체 공개' : '관계자만 접근',
      '노출 상태': data.is_exposure ? '노출' : '숨김',
      '강의 상태': classStatusText(data.class_status),
      '강의 학기': data.semester,
      '강의 기간': data.live_period,
      '강의 횟수': data.num_lives,
      '상품 분류': data.class_group,
      '제공 형태': data.class_form,
      '강의 개설일': data.created_at,
      '마지막 수정일': data.updated_at ?? '-',
    }, 'add')

      setIsCheck(true)
    } else {
      pushItemToBoxDataArr(
        {
          'index': index,
        },
        'delete'
      )
      
      setIsCheck(false)
    }
  }
  
<div className='learner_check_box'>
   <input
         type='checkbox'
         id={`learner_check_checkbox ${index}`}
         onClick={switchCheckedClass}
         checked={isCheck} />
   <label htmlFor={`learner_check_checkbox ${index}`} />
</div>
  1. map 메서드로 돌린 자식 컴포넌트의 경우, input의 id과 label의 htmlFor가 동일해지면, 해당 항목 체크를 했을 때 전체 항목이 체크가 될 수 있다. 따라서 index로 차별화를 해두었다.
  2. 위에서와 동일하게, 체크박스 input의 checked 속성은isCheck state로 관리한다.
  3. 체크박스를 클릭하면 switchCheckedClass 온클릭 이벤트가 발생한다.
  • isCheck가 true로 바뀐다.
  • pushItemToBoxDataArr 를 'add'일 경우로, 해당 객체 정보를 파라미터로 담아 호출한다.
  1. 체크박스 해제시, isCheck가 false로 바뀐다.
    -pushItemToBoxDataArr 를 'delete'일 경우로, 해당 객체의 인덱스로 파라미터로 담아 호출한다. (인덱스로 삭제를 처리하기 때문이다!)

4. useEffect로 체크박스 관리하기

  useEffect(() => {
    if (isCheckAll) {
      setIsCheck(true)
      pushItemToBoxDataArr({
      'index': index,
      '강의 명': data.name,
      '강사 명': data.instructor_nickname,
      'URL 접근 권한': data.is_public ? '전체 공개' : '관계자만 접근',
      '노출 상태': data.is_exposure ? '노출' : '숨김',
      '강의 상태': classStatusText(data.class_status),
      '강의 학기': data.semester,
      '강의 기간': data.live_period,
      '강의 횟수': data.num_lives,
      '상품 분류': data.class_group,
      '제공 형태': data.class_form,
      '강의 개설일': data.created_at,
      '마지막 수정일': data.updated_at ?? '-',
      },
        'all'
      )
    } else {
      setIsCheck(false)
    }
  }, [isCheckAll])
  • 부모 컴포넌트에서 모든 항목에 체크를 했을 경우, 자식 컴포넌트의 체크 state도 켜진다.
  • pushItemToBoxDataArr 메서드도 'all' 상태로, 해당 항목의 정보를 담아 호출한다.
  • 체크 해제시, 자식 컴포넌트의 체크 state 또한 꺼진다.
profile
FE Developer

5개의 댓글

comment-user-thumbnail
2022년 11월 28일

I simply began reading your io games article. This web site truly thrills me, and I excitedly await your next write-up.

답글 달기
comment-user-thumbnail
2023년 4월 17일

지난 튜토리얼에서 우리는 input 상태를 관리하는 방법에 대하여 알아보았는데요 cookie clicker, 이번에는 input 이 여러개일때는 어떻게 관리해야 하는지 알아보겠습니다.

답글 달기
comment-user-thumbnail
2023년 6월 14일

위의 예제에서는 isChecked 상태에 따라 체크박스의 선택 여부를 표시하고 있습니다. checked 속성에 isChecked 변수를 바인딩하여 상태를 표시하고, onChange rankdle 이벤트 핸들러에서 isChecked 상태를 업데이트합니다.

답글 달기
comment-user-thumbnail
2023년 9월 29일

Reading your piece about geometry dash lite just now. I am genuinely thrilled by your website, and I eagerly anticipate your future post.

답글 달기
comment-user-thumbnail
2023년 10월 30일

What happens if a child component's Checkbox is turned into a map? I think that would be bad ice cream

답글 달기