Front-End/React

[React] Checkbox

engine 2022. 4. 6. 14:55

종합설계 프로젝트에서, 체크박스의 상태관리 및 체크박스 전부 체크 시 버튼을 활성화하는 부분을 구현하는데 애를 먹었다.

여러 방법이 있지만, useState를 활용하여 상태관리를 하는 방법을 남겨보고자 한다.

 

체크박스가 하나라도 선택되지 않은 상태: 확인 버튼 비활성

체크박스 전부 선택시 안내문구 사라짐 및 버튼 활성화

const SignupContents = ({ history }) => {
  const [checkedButtons, setCheckedButtons] = useState([]);

  const changeHandler = (checked, id) => {
    if (checked) {
      setCheckedButtons([...checkedButtons, id]);
      console.log(체크);
    } else {
      setCheckedButtons(checkedButtons.filter(button => button !== id));
      console.log(체크 해제);
    }
  };

  const isAllChecked = checkedButtons.length === 2;
  const disabled = !isAllChecked;

우선 체크박스가 체크되었는지를 확인하는 과정이 먼저 필요했다. 그래서, checkedButtons에 대해 useState를 해준다.

1) check가 되었을 경우 checkedButtons에 id를 추가하고,

  - 여기서 ...는 spread 연산자로, 자세한 설명은 여기를 참고하기 바란다.

2) check가 해제되었을 경우(else문) checkedButtons 배열 형태에서 id를 삭제했다.

checkbox가 두개밖에 없기 때문에 length === 2라는, 썩 좋은 코드라고 보기 힘든 방식으로 전부 체크가 되었는지를 검사했다.

check의 여부를 전부 false로 두고 시작하는 useState문의 경우에는 다른 방식으로 풀 수 있었으나, 일단 좀 빠르게 하기 위해 길이가 2일 때를 변수로 두었다. 그리고, 이 조건을 충족하지 않은 경우를 disabled라는 변수에 저장했다.

return (
  <>
      <input
      type="checkbox"
      id="check"
      onChange={e => {
        changeHandler(e.currentTarget.checked, 'check');
      }}
      checked={checkedButtons.includes('check') ? true : false}
      ></input>
      <label id="check" htmlFor="check"></label>
      <span>동의합니다</span>

    <input
      type="checkbox"
      id="check2"
      onChange={e => {
        changeHandler(e.currentTarget.checked, 'check2');
      }}
      checked={checkedButtons.includes('check2') ? true : false}
      ></input>
      <label id="check2" htmlFor="check2"></label>
      <span>동의합니다</span>
  </>

그 다음, 두 가지의 input에 대해 onChange로 위에서 선언한 changeHandler에 들어갈 조건들을 넣었다.

현재 타겟이 체크되었는지를 확인하는 e.currentTarget.checked를 사용하기 위해 파라미터로 e를 넣었다. id값은 두 개의 상태를 따로 관리하기 위해 check과 check2로 나누었다.

checked의 경우는, 위에서 선언한 checkedButtons에  check가 있으면 true, 없으면 false를 삼항 연산자로 부여했다.

그 아랫줄의 label은, 체크박스 스타일링을 위한 것으로, 보통 체크박스를 스타일링할 때는 기존의 checkbox input을 display:none 으로 없앤 뒤 그 위에 label을 디자인한다. 이러한 이유로 label을 사용하였다.

(label의 경우 React에서는 for 대신 htmlFor을 사용한다. 에러 로그에 친절하게 알려주기 때문에 꼭 미리 알 필요는 없다)

    <div id="description">
      <h2 style={disabled ? { display: 'block' } : { color: 'white' }}>
        <img
          style={
            disabled ? { display: 'inline-block' } : { display: 'none' }
          }
          src={redcheck}
          id="redcheck"
          alt="redcheck"
        />
        앗! 필수 동의 항목에 동의하지 않으셨어요!
      </h2>
    </div>
    <ButtonWrap>
      <Button>
        <h2>취소</h2>
      </Button>
      <Button
        disabled={disabled}
        onClick={() => history.push('/signupcomplete')}
        style={
          disabled
            ? { backgroundColor: '#859594' }
            : { backgroundColor: '#F79C43' }
        }
      >
        <h2>확인</h2>
      </Button>
    </ButtonWrap>

이제 전부 체크되었을 때 버튼을 활성화하고  안내문구가 사라지게 하는 방법을 알아보자. 이 쪽은 사실 앞에서 제대로 했다면 어려울 게 없는 부분이다.

description div에는 빨간색 체크 이미지와 글자가 들어갔고,  styled-components를 사용한 ButtonWrap에는 두 가지 버튼이 들어갔다. styled-components는 이 기능 구현에선 그렇게 중요하지 않은 부분이므로 코드를 따로 가져오진 않았다.

 

첫 번째 코드에서 선언한 disabled를 사용한다. 문구가 사라지게 하는 경우는 그냥 삼항 연산자를 사용하여, disabled일 경우 display: block으로 기존 위치에 있게 하였고, disabled가 아닌 경우, 즉 체크표시를 2개 다 한 경우에는 display: none이 아닌 color: white로 줬다. display: none을 주면 그 만큼의 height가 사라지면서 아래 버튼이 그 height만큼 위로 쑥 올라온다. 그것을 방지하기 위해 일단 color: white로 줬다. h2 안에 있는 이미지의 경우, 색상을 변경한다고 해서 이미지 자체의 색상이 변하지 않기 때문에 display: none을 해줬다. 이것을 감싸는 h2의 height보다 그 값이 작기 때문에 none 처리를 해줘도 전체 높이에 변함이 없다.

 

그 다음으로 버튼은, 우선 signupcomplete라는 URL로 push하는 history.push를 사용하였고(이 역시 여기선 중요한 게 아니므로 따로 설명하지는 않는다.), 위의 이미지를 포함한 텍스트와는 다르게 이 버튼은 활성화가 되어있지 않다가 두 체크박스를 다 선택했을 때 활성화가 되어야 한다. 그래서, disabled={disabled}로, 비활성화되는 조건을 제일 위에서 선언한 disabled를 넣어 줬다.

 

활성화되었을 때와 그렇지 않은 때를 구분하기 위해 색상도 disalbed의 여부에 따라 달라지게끔 해줬다.

 

 

3