종합설계 프로젝트에서, 체크박스의 상태관리 및 체크박스 전부 체크 시 버튼을 활성화하는 부분을 구현하는데 애를 먹었다.
여러 방법이 있지만, 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의 여부에 따라 달라지게끔 해줬다.