게시판 목록-2 : CheckBox

이병관·2021년 5월 19일
0

CheckBox

게시판에서 이벤트를 부여할때 조금 어려운 부분이 바로 체크박스 부분이 아닐까 싶다.
UseState, UseState말을 들어도 동적으로 페이지를 변환시킨다는 부분은 피부로 잘 와닿지 않는다.
그렇기에 두가지 방법으로 체크박스를 핸들링하는 방법이 있다.

useState 객체로 체크박스 일괄,개별 선택하기


useState는 값을 변화 시키고 원하는 부분에 대해 리랜더링을 할 수 있도록 도와주는 함수형 컴포넌트중 하나이다.useState를 이용하면 배열이나 객체의 값을 원하는대로 조정할 수 있다.

이를 이용하면 체크박스 역시 손쉽게 조정이 가능하다.

체크박스를 불러온 데이터를 통해 동적으로 그려주는 코드를 보자

 {data.fetchBoards.slice(0,10).map(data => (
        <Table_Body_literable>
          <Table_Body_Checkbox ><input id={data.number}  type="checkbox" onClick={handleCheck} checked={checked[data.number]}></input></Table_Body_Checkbox>
          <Table_Body_Number >{data.number}</Table_Body_Number>
          <Table_Body_Title>{data.title}</Table_Body_Title>
          <Table_Body_Date>{data.createdAt.slice(0,10).replace(/-/gi, '.')}</Table_Body_Date>
        </Table_Body_literable>
        ))}

Props의 디스트럭처링 할당을 통해 gql query로 불러온 data라는 항목 중, fetchBoards의 데이터를 10개만 가져 온 후, 그것을 Map.()함수를 통해 동적으로 그려 준다고 했을때

하나의 checkbox에 대해 datanumber로 아이디를 할당한다. 이는 check상태를 바꾸기 용이하게 하기 위해 부여하는것이다. 또한 onClick이벤트와 checked={checked[data.number]}를 주어 눌렀을때 이벤트와, 체크가 되었는지 안되어있는지 설정한다.

handleCheck(e)

useState로 값을 용이하게 변경하기 위해 일단 현재 상태를 저장하는 useState()를 선언하자.
그리고 헤더부분의 체크박스 부분 역시 useState로 저장한다.

const [checked, SetChecked] = useState({ //각 버튼들의 체크 상태 기록. });

const[checkedAll, setCheckedAll] = useState(false);//헤더 부분 체크박스

checked의 기본값을 배열로 잡는다. 왜 배열로 잡는지 찬찬히 아래로 내려가며 설명하겠다.

const handleCheck = (e) => {
   ////체크 상태 변경
   console.log(e.target.checked)
   const newChecked = {...checked, [e.target.id] : e.target.checked}
   SetChecked(newChecked);
   console.log(newChecked);
   ///

   const values = []
   for(let i = 0 ; i<data.fetchBoards.length; i++){
     values.push(newChecked[data.fetchBoards[i].number]); //새로운 배열에 값을 저장.
   }
   const filterValues = values.filter(data => data === true)//체크된 친구들만 저장해주자, 근데 길이의 값이 서로 같다면? 모조리 체크되어 있는것이다.
   if(data.fetchBoards.length === filterValues.length){ //모조리 체크 된 상태라면 헤더의 체크박스를 처리해버리자.
     setCheckedAll(true);
   } else {
     setCheckedAll(false);
   }
 }

const newChecked = {...checked, [e.target.id] : e.target.checked}를 선언한다. newCheckeduseState로 선언한 기본값인 checked의 배열을 복사 한 후, 만일 눌렀을 때 해당 체크박스의 id값이 존재하지 않는다면 해당 체크박스의 id를 키값으로 설정, 그 체크박스가 눌렸는지 안눌렸는지 e.target.checked의 값으로 저장한다.

즉 만일 배열에 존재하지 않는 id = 101인 체크박스를 눌렀을경우 id: 101, true 상태가 들어가게 되는 것이다.

그리고 SetChecked(newChecked);로 이전에 checkednewChecked로 바꾸게 되고, 다음 for문을 실행하게 된다.

const values = []
    for(let i = 0 ; i<data.fetchBoards.length; i++){
      values.push(newChecked[data.fetchBoards[i].number]); //새로운 배열에 값을 저장.
    }

선언한 values 배열에 for문을 통해 불러온 data.fetchBoards의 데이터의 길이만큼 values 배열에 저장 한 뒤, Filter.()함수를 통해 체크된 배열만 반환하여 filterValues에 저장한다.

이를 하는 이유는 맨 위 헤더 부분 역시 만약 바디 부분의 체크박스가 모두다 체크 될 경우 헤드 부분의 체크박스 역시 체크가 되도록 상태를 변하게 만들어야 하기 때문이다.

따라서 만약 true값인 id만 가져온 배열, filterValues의 길이가 미리 가져온 data.fetchBoards의 길이가 같다면 이는 바디 부분의 체크박스가 모두 체크되었다는 말이기에 그때 setCheckedAll(true);를 하여 헤드 체크박스를 바꿔준다.

이제 바디부분의 체크박스의 checked상태는 위에서 정의한 '상태'와 똑같아야 하기때문에
checked={checked[data.number]}까지 넣어 주게 된다면 체크와 체크해제에 따라 checked배열이 변화하고, 체크박스의 상태 역시 변한다.

여기까지가 바디 부분에서 체크박스 클릭시 헤드 부분까지 변화를 주는 부분이다.

이제 만약 헤드부분의 체크박스를 클릭 시, 바디부분의 모든 체크박스를 체크하거나 체크 해제를 하게 일괄변경 하려면 어떻게 해야할까?

handleCheckAll(e)

const handleCheckAll = (e) => {
    const newCheckAll = e.target.checked
    if(newCheckAll){
      let newCheck = {};
      for(let i=0; i<data.fetchBoards.length ; i++){
        newCheck = {...newCheck, [data.fetchBoards[i].number] : true};
      }
      console.log(newCheck);
      SetChecked(newCheck);
      setCheckedAll(true);

    } else {
      let newCheck = {};
      for(let i=0; i<data.fetchBoards.length ; i++){
        newCheck = {...newCheck, [data.fetchBoards[i].number] : false};
      }
      console.log(newCheck);
      SetChecked(newCheck);
      setCheckedAll(false);
    }
    
  } 

위의 바디부분을 구현했다면 일괄선택, 선택해제는 비슷하게 생각하자.

handleCheckAll의 경우에는 새로운 변수 const newCheckAll = e.target.checked를 선언한다.
여기서 생각해보면 newCheckAll은 헤더의 체크박스값만 확인하면 되기 때문에 따로 배열을 선언하지않고 해당 값을 바로 if문에 넣어 판별식을 적어 나가자.

즉 만약 newCheckAllTrue값. 체크된 상태라면,

let newCheck = {};
      for(let i=0; i<data.fetchBoards.length ; i++){
        newCheck = {...newCheck, [data.fetchBoards[i].number] : true};
      }

배열 newCheck에 현재 가져온 데이터의 체크박스에 해당하는 모든 id값들을 true상태로바꾸어 버린다.
여기서 Spread연산자로 newCheck를 넣은 이유는 반복문을 돌리면서 계속해서 값을 넣어나가기 때문이다.
그 뒤 SetCheckedsetCheckedAll에 원하는 값만 넣어주면 끝이다.


잘 작동한다. 이처럼 객체로 저장할 수 있으나 그 방법이 싫다면 간단하게 배열로 처리하는 방법도 있다.

useState 배열로 일괄,개별선택하기


일단 변수 선언부분은 설명을 생략하겠다

const[checkedAll, setCheckedAll] = useState(false);
const[checked, SetChecked] = useState([]); //이번엔 배열로.

handleClick(e)

const handleCheck = (e) => {
    const number = Number(e.target.id);
    ////체크 상태 변경
    let newChecked = [];
    if(checked.includes(number)){
      newChecked = checked.filter(data => data !== number)
      SetChecked(newChecked);
      console.log(checked);
    } else {
      newChecked = [...checked, number]
      SetChecked(newChecked);
      console.log(checked);
    }
    ///

    if(data.fetchBoards.length === newChecked.length){
      setCheckedAll(true);
    } else {
      setCheckedAll(false);
    }
  }

배열은 생각보다 간단한데,
일단 newChecked라는 하나의 비어있는 배열과 const number = Number(e.target.id);라는 클릭한 것의 아이디를 저장해주는 변수를 선언해준다. 그렇다면 배열에 들어가 있다는 말은 즉 해당하는 아이디의 체크박스는 클릭된 상태란 뜻이다

만일 선택한 체크박스가 체크되있는 상태가 false상태라면 newChecked에 해당 배열을 넣어주고 SetChecked라는 이미 선언한 useState를 통해 상태를 변경해주고, 이미 체크된 상태라면 checked의 배열에서 해당 아이디값을 filter를 통해 제거한다.

왜 필터로? : 원본데이터를 직접 조작하는건 불변성을 깨트리는 행위이기에.

handleCheckAll (e)

  const handleCheckAll = (e) => {
    const newCheckAll = e.target.checked
    if(newCheckAll){
      const newCheck = data.fetchBoards.map(data => data.number)
      SetChecked(newCheck);
      setCheckedAll(true);

    } else {
      const newCheck =[]
      SetChecked(newCheck);
      setCheckedAll(false);
    }
  } 

일괄 선택, 해제 역시 간단하게 구현이 가능한데, 만일 헤더부분의 체크박스를 클릭시(newCheckAll === true) SetCheckedchecked의 배열에 모든 아이디 값들을 넣고, setCheckedAlltrue상태로 만들고, 만일 해제 할 시, 빈배열을 checked에 넣어주고 setCheckedAll역시 false로 설정하는것이다.

이제 이벤트는 모두 부여했다. 하지만 이것으로 true와 false로 check의 상태를 바꿔주기 위해선 어떻게 해야할까?

checked={checked.includes(data.number)}

바로 includes의 함수를 이용하면 매우 손쉽게 변환해버릴수있다.
만약 해당 배열에(checked) 원하는 배열의 요소(data.number)가 있다면(includes) true로, 없다면 false를 반환해 줄것이기 때문이다.

이것이 바로 useState를 사용하여 배열형식으로 만드는 방식이다.

마치며

체크박스는 아주 간단한 친구일줄 알았는데 조금 깊게 파고 들어가다보니 쉬운 친구가 아니라는것을 알게되었다. 주말에 체크박스에 관해 좀더 정리를 해봐야할것같다.

profile
뜨겁고 매콤하고 화끈하게

0개의 댓글