모티브로 삼았던 페이지의 회원가입이 두 페이지로 나눠져있어
우리도 동일하게 진행을 하기로 했다.
회원가입도 금방하겠지라고 생각을 했었는데
약관동의가.. 엄청나게 큰 산이었다.
하나의 state로 관리를 하지만 체크박스 하나하나가 따로 작동을 해야하고
전체동의 체크박스를 누르면 또 같이 연동되어 움직어야하는 부분이
너무 어렵게 느껴졌었다ㅜㅜ
그래서 멘토님의 말씀대로 손코딩을 하면서 진행해보았다.
최종적으로 첫번째 페이지에서 내가 구현해야하는 내용을 글로 적어보자면 아래와 같다.
1. 전체동의 체크박스 클릭 시 모든 체크박스가 체크 될 것
2. 하나라도 해제하면 전체동의 체크박스가 해제 될 것
3. 필수 체크박스만 체크하더라도 버튼이 활성화 될 것
일단 먼저 state로 체크박스를 관리해준다.
계산된 속성명을 사용해 하나의 스테이트 안에 여러 체크박스를 객체로 관리해주었다.
그리고 리뷰를 받으면서 배열로 관리하는게 더 쉬웠을 수도 있다는 말을 들었다.
리팩토링 할 때 배열로 구현해보려고 한다.
const [isAll, setIsAll] = useState(false);
const [isChecked, setIsChecked] = useState({ 1: false, 2: false, 3: false });
const handleChecked = e => {
const { id, checked } = e.target;
setIsChecked({ ...isChecked, [id]: checked });
};
<input
id="all"
type="checkbox"
checked={isAll}
onChange={e => allChecked(e)}
/>
{AGREEMENT.map(data => {
return (
<AgreementMap
key={data.id}
data={data}
checked={isChecked[data.id]}
handleChecked={handleChecked}
/>
);
})}
전체동의 체크박스 클릭 시 모든 체크박스가 체크 되는
기능을 구현하기 위해 전체동의 체크박스의 onclick에 들어갈 함수를 하나 만들어주었다.
전체동의 체크박스의 값이 바뀌면 나머지 체크박스의 값도 동일한 값을 바꿔준뒤
새로운 객체에 넣어두고 그 새로운 객체를 나머지 체크박스의 값으로 넣어주는 방식으로 구현하였다.
리액트에서는 for문을 잘 사용하지 않는 다고 들었는데 for문을 사용해서 왠지 진 기분이 들었다.
그리고 Object.entries()라는 메서드를 알게 되었고 리팩토링할 때 적용해볼 예정이다.
const allChecked = e => {
const { checked } = e.target;
setIsAll(prev => !prev);
let checkedObj = {};
for (let i = 0; i <= AGREEMENT.length - 1; i++) {
checkedObj = { ...checkedObj, [AGREEMENT[i].id]: checked };
}
setIsChecked(checkedObj);
};
<input
id="all"
type="checkbox"
checked={isAll}
onChange={e => allChecked(e)}
/>
하나라도 해제하면 전체동의 체크박스가 해제 되고 나머지 체크박스가 모두 체크되었을 때
전체동의 체크박스도 체크될 수 있도록 기능을 구현하였다.
먼저 나머지 체크박스들의 값을 확인해 true인 것을 filter할 수 있도록 했고
filter한 값의 길이가 3이면 전체동의 체크박스의 값이 바뀌도록 했다.
이 때 side effect가 발생하는것을 control하고자 useEffect로 감싸주었고
의존성배열에는 isAllChecked를 넣어주어 이 값이 바뀔 때 실행되도록 해주었다.
const isAllChecked = Object.values(isChecked).filter(el => el === true);
useEffect(() => {
setIsAll(isAllChecked.length === 3);
}, [isAllChecked]);
마지막으로 필수 체크박스만 체크하더라도 버튼이 활성화 할 수 있도록 구현했다.
필수 항목이었던 체크박스를 불러와 논리연산자를 사용해 조건을 걸어주었고
그 조건을 disabled와 style 속성에 사용했다.
그리고 버튼을 누르면 페이지를 이동할 수 있도록 link 태그를 걸어주었다.
const isValid = isChecked[1] && isChecked[2];
<Link to="/signUpSecond">
<button
className="phone"
disabled={isValid ? false : true}
style={{ opacity: `${isValid ? 1 : 0.5}` }}
>
다음 페이지로
</button>
</Link>
아래는 전체 코드이다.
AGREEMENT 파일은 상수데이터 파일이고
AgreementMap 파일은 input checkbox를 컴포넌트로 빼둔 파일이다.
상수데이터를 사용했던 이유는 약관 내용이 길기도 했고 같은 모양을 가지고 있기 때문이고
컴포넌트를 따로 빼둔 이유는 컴포넌트 분리를 연습해보기도 하고 이 역시 같은 모양을 가지고 있기 때문이었다.
import './SignUp.scss';
import { Link } from 'react-router-dom';
import { AGREEMENT } from './AGREEMENT';
import { useEffect, useState } from 'react';
import AgreementMap from '../components/AgreementMap';
const SignUp = () => {
const [isAll, setIsAll] = useState(false);
const [isChecked, setIsChecked] = useState({ 1: false, 2: false, 3: false });
const handleChecked = e => {
const { id, checked } = e.target;
setIsChecked({ ...isChecked, [id]: checked });
};
const isAllChecked = Object.values(isChecked).filter(el => el === true);
const allChecked = e => {
const { checked } = e.target;
setIsAll(prev => !prev);
let checkedObj = {};
for (let i = 0; i <= AGREEMENT.length - 1; i++) {
checkedObj = { ...checkedObj, [AGREEMENT[i].id]: checked };
}
setIsChecked(checkedObj);
};
const isValid = isChecked[1] && isChecked[2];
useEffect(() => {
setIsAll(isAllChecked.length === 3);
}, [isAllChecked]);
return (
<div className="signUp">
<div className="fullContainer">
<p className="title">회원가입</p>
<div className="border" />
<div className="fullAgreement">
<p className="subTitle">약관동의</p>
</div>
<div className="full">
<input
id="all"
type="checkbox"
checked={isAll}
onChange={e => allChecked(e)}
/>
<p>전체 동의합니다.</p>
</div>
<p>
선택 항목에 동의하지 않은 경우도 회원가입 및 서비스 이용이 가능합니다.
</p>
<div className="border" />
{AGREEMENT.map(data => {
return (
<AgreementMap
key={data.id}
data={data}
checked={isChecked[data.id]}
handleChecked={handleChecked}
/>
);
})}
<Link to="/signUpSecond">
<button
className="phone"
disabled={isValid ? false : true}
style={{ opacity: `${isValid ? 1 : 0.5}` }}
>
다음 페이지로
</button>
</Link>
</div>
</div>
);
};
export default SignUp;
'🍀오늘도 삽질 중🍀 > React.js' 카테고리의 다른 글
토큰 유무에 따라 로그인/로그아웃 구현하기 (0) | 2023.07.15 |
---|---|
정규표현식을 이용한 sign up 구현하기-2 (0) | 2023.07.13 |
간단하게 Sign In 구현하기 (0) | 2023.07.11 |
fetch 메소드로 백엔드랑 소통하기 (0) | 2023.06.22 |
댓글 컴포넌트화해 분리하고 props로 데이터 전달하기 (0) | 2023.06.19 |