[리팩토링] useCheckBox 훅 구현

yoon Y·2022년 2월 8일
2

[3rd_Project] MonthSub

목록 보기
8/11

기존 CheckBox는 다른 컴포넌트에 의존성이 높고 기능도 간단했기 때문에
기능을 추가하고, hook을 만들어서 로직을 분리, 재사용할 수 있게 리팩토링 했다

  • 전체 선택 항목 추가
  • 전체 선택를 누를 시 다른 항목들의 체크가 풀리는 기능
  • 전체 항목을 제외한 다른 모든 항목을 체크했을 때 자동으로 전체 항목이 선택되고 나머지는 풀리는 기능
  • input으로 사용하는 것이 아닌 필터로 사용할 경우의 기능 추가(props으로 설정할 수 있음)
    • 디폴트는 전체항목으로 체크되어있게 설정
      • 다른 항목이 체크되어있지 않은 경우 전체체크가 풀리지 않음
      • 아무 항목에도 체크하지 않을 경우 자동으로 전체로 항목으로 체크됨



  1. 전체 항목 요소와 개별 항목 요소들을 분리해서 만들고 이벤트 핸들러 함수도 각각 만들어야한다.

  2. checked의 초기값도 상태를 기준으로 설정해야한다. (여기에서 많이 헤맸음)
    - checked도 어찌됐든 스타일을 결정하는 속성이기 때문.
    - 상태가 기준이 아닌 그냥 초기값을 false로 설정할 시 활성화가 안됐음.
    - 개별 항목 요소의checked 기준은 상태(배열)안에 그 요소의 값이 포함되어있는지,
    전체 항목 요소의 checked 기준은 모든 항목의 갯수와 체크된 항목의 갯수가 같은지로 설정해야한다.

    // 오류 났던 코드 - checked의 초기 값을 상태에 따른 ture or false가 아닌 null로 설정했기 때문
    return (
      <div>
        <label label htmlFor="all">
          <input
            type="checkbox"
            id="all"
            onChange={handleCheckedAll}
            checked={checkedList.length === valueList.length ? true : null}
          />
          all
        </label>
        {dataList.map(({ id, value }) => (
          <label key={id} htmlFor={value}>
            <input
              key={id}
              id={value}
              type="checkbox"
              value={value}
              onChange={handelCheckedElement}
              checked={ // check된 값들 중 해당 요소의 값이 포함된지만 확인하면 되는 지 몰랐다
                checkedList.length === valueList.length
                  ? false
                  : checkedList[0] === value
                  ? true
                  : null
              }
            />
            {value}
          </label>
        ))}
      </div>
  3. 부모 컴포넌트에게 상태를 전달해줘야하는데 상태가 변화할 때마다 전해줘야해서 useEffect 안에 onChange(state)함수를 실행하고 전해줄 state를 의존성으로 걸어주었다.




최종 코드

useCheckBox.tsx


import { useState, useEffect } from 'react';

const useCheckBox = ({
  name = '',
  valueList,
  initialCheckeds,
  filterMode,
  onChange,
}) => {
  const [checkedList, setCheckedList] = useState(initialCheckeds);

  useEffect(() => {
    onChange && onChange(name, checkedList);
  }, [checkedList]);

  const handleCheckedAll = e => {
    const { checked } = e.target;
    if (checked) {
      setCheckedList(valueList);
    } else {
      !filterMode && setCheckedList([]);
    }
  };

  const handleCheckedElement = e => {
    const { value, checked } = e.target;
    if (checked) {
      checkedList.length === valueList.length
        ? setCheckedList([value])
        : setCheckedList([...checkedList, value]);
    } else {
      if (filterMode && checkedList.length === 1) {
        setCheckedList(valueList);
        return;
      }
      setCheckedList(checkedList.filter(checkedItem => checkedItem !== value));
    }
  };

  return {
    checkedList,
    setCheckedList,
    handleCheckedAll,
    handleCheckedElement,
  };
};

export default useCheckBox;

DaySelect.tsx


return (
    <div {...props}>
      <label htmlFor="all">
        <StyledInput
          type="checkbox"
          id="all"
          onChange={handleCheckedAll}
          checked={checkedList.length === valueList.length}
        />
        <StyledButton>전체</StyledButton>
      </label>
      {valueList.map(value => (
        <label key={value} htmlFor={value}>
          <StyledInput
            id={value}
            type="checkbox"
            value={value}
            onChange={handleCheckedElement}
            checked={
              checkedList.length === valueList.length
                ? false
                : checkedList.includes(value)
            }
          />
          <StyledButton>{convertDay([value])}</StyledButton>
        </label>
      ))}
    </div>
  );
profile
#프론트엔드

0개의 댓글