front/react

[Refactor] checkbox useState,onChange 이벤트 react-hook-form으로 수정

jeong_ga 2022. 11. 30. 10:51

useState로 작성하다 중간에 react-hook-form을 추가했던 거라 input type text, number 등은 react-hook-form으로 작성했지만 radio와 checkbox는 useState로 작성되어 있었다.

API가 추가될 동안 해당 기능을 리팩토링하고자 하는 중, 과하게 복잡하게 작성했던 onChange이벤트를 발견했다.

 

 

[Feat] input type checkbox 추가하여 복수선택 가능하도록 설정 & 복수 선택하여 submit 이벤트 진행

필요한 기능 목록에서 복수선택하여 해당 회원 정보를 변경할 수 있도록 설정 복수선택해야 하는 항목이 여러개일 경우 하나의 항목만 선택하도록 조건 추가 회원정보 상세보기 화면에서 input t

jeong-ga.tistory.com

 

입력 이벤트를 onChange로 작성했던 코드를 react-hook-form으로 변경하는 작업을 진행하며 기존에 작성했던  checkbox 클릭 이벤트를 수정하였다.

 

// radio, checkbox useState
const [detailComapanyRadio, setDetailComapanyRadio] = useState({
   useFlag: "1",
   status: "2",
   gongsaType: "reser",
 });
 
 
 
 //  checkbox, 복수 선택 이벤트
  const handleOnchangeGonsatype = (e) => {
    const INVERTORARR = detailComapanyRadio.gongsaType.split(",");
    let arr = [];
    if (INVERTORARR.length == 1) {
      if (detailComapanyRadio.gongsaType !== e.target.value) {
        arr = [detailComapanyRadio.gongsaType, e.target.value];
      }
    } else {
      arr = [...INVERTORARR];
      if (arr.includes(e.target.value)) {
        arr = arr.filter((it) => it !== e.target.value);
      } else {
        arr.push(e.target.value);
      }
    }
    arr = arr.filter((it) => it !== "");
    setDetailComapanyRadio({
      ...detailComapanyRadio,
      gongsaType: arr.toString(),
    });
  };



// return 코드
    <div className="formContentWrap">
              <div className="blockLabel">
                <span>사업자 공사 관리</span>
              </div>

              <div className="formPaddingWrap">
                <input
                  type="checkbox"
                  value="emer"
                  id="emer"
                  name="gongsaType"
                  className="listSearchRadioInput"
                  checked={detailComapanyRadio.gongsaType.includes("emer")}
                  onChange={handleOnchangeGonsatype}
                />
                <label htmlFor="emer" className="listSearchRadioLabel">
                  긴급
                </label>
                <input
                  type="checkbox"
                  value="inday"
                  id="inday"
                  name="gongsaType"
                  className="listSearchRadioInput"
                  checked={detailComapanyRadio.gongsaType.includes("inday")}
                  onChange={handleOnchangeGonsatype}
                />
                <label htmlFor="inday" className="listSearchRadioLabel">
                  당일
                </label>
                <input
                  type="checkbox"
                  value="reser"
                  id="reser"
                  name="gongsaType"
                  className="listSearchRadioInput"
                  checked={detailComapanyRadio.gongsaType.includes("reser")}
                  onChange={handleOnchangeGonsatype}
                />
                <label htmlFor="reser" className="listSearchRadioLabel">
                  예약
                </label>
              </div>
            </div>

 

위 코드처럼 onChange이벤트를 통해 useState를 수정하는 코드를 작성했는데... 쓰면서도 이게 이렇게 복잡한 일인가 싶었지만.. 🥲

어쨌든 react-hook-form으로 작성하니 아주 편리해졌다.

 

// axios 시
servicesPostData(urlSetCompanyDetail, {
      rcid: cid,
      useFlag: getValues("_detailUseFlag"),
      gongsaType: getValues("_gongsaType").toString(),
      status: getValues("_status"),
      ...



// return 코드
<div className="formContentWrap">
      <div className="blockLabel">
        <span>사업자 공사 관리</span>
      </div>

      <div className="formPaddingWrap">
        <input
          type="checkbox"
          value="emer"
          id="emer"
          className="listSearchRadioInput"
          checked={
            getValues("_gongsaType") &&
            watch("_gongsaType").includes("emer")
          }
          {...register("_gongsaType")}
        />
        <label htmlFor="emer" className="listSearchRadioLabel">
          긴급
        </label>
       	
        ...
        
      </div>
    </div>

onChange이벤트를 따로 작성할 필요 없이 배열인 값을 string으로 변환해서 서버에 전달해주면 된다.

 

 

하위 컴포넌트가 없는 검색의 경우 훨씬 간단한 코드가 되었다.

더보기

수정 전

import { useState, useEffect } from "react";
import { useForm } from "react-hook-form";

import { servicesPostData } from "../../Services/importData";
import { urlUserlist } from "../../Services/string";

export default function ComponentListUserSearch({
  setUserList,
  setListPage,
  searchClick,
  setSearchClick,
  page,
}) {
  // react-hook-form 라이브러리
  const { register, reset, handleSubmit } = useForm({});

  const [searchData, setSearchData] = useState({
    userrole: "ROLE_USER",
    useFlag: "1",
  });

  useEffect(() => {
    // searchClick을 클릭한 (true) 상태에서 동작
    searchClick === true && SearchSubmit();
  }, [page.activePage || searchClick]);

  // 상위 컴포넌트에게 전달받은 useState의 set 함수
  // setUserList가 set으로 전달받은 후 사용하기 위해 && 사용
  const userList = (res) => {
    setUserList && setUserList(res);
  };
  const listPage = (res) => {
    setListPage && setListPage(res);
  };

  // submit 이벤트
  function SearchSubmit() {
    const searchDataObj = Object.entries(searchData);
    // input에 입력된 값을 없애도 useState에 담긴 키가 사라지지 않아, 해당 값이 빈칸이라면 키를 제거하기 위해 filter 사용
    const searchDataFilter = searchDataObj.filter((it) => it[1] !== "");
    const searchDataReq = Object.fromEntries(searchDataFilter);

    servicesPostData(urlUserlist, {
      offset: page.getPage,
      size: 15,
      ...searchDataReq,
    }).then((res) => {
      if (res.status === "fail") {
        alert("검색하신 데이터가 없습니다.");
      }
      if (res.status === "success") {
        userList(res.data);
        listPage(res.page);
        setSearchClick(true);
      }
    });
  }

  // 입력 이벤트
  function onChangeHandle(e) {
    // input Type = "date"
    if (e.target.id === "createTime") {
      // 날짜 포맷에 맞게 전송하기 위해 처리
      const pickDate = new Date(e.target.value);
      setSearchData({
        ...searchData,
        [e.target.id]: pickDate.toISOString().slice(0, 19),
      });
    }
    setSearchData({
      ...searchData,
      [e.target.name.slice(1)]: e.target.value,
    });
    // }
  }

  // 초기화 이벤트
  function onResetHandle(e) {
    reset();
    setSearchData({});
    setSearchClick(false);
  }
  // formWrap

  return (
    <div className="commonBox">
      <h3 className="blind">사업자관리 검색 필터</h3>
      <form
        className="listSearchForm formLayout"
        onSubmit={handleSubmit(SearchSubmit)}
      >
        <div className="listSearchWrap">
          <label className="blockLabel">
            <span>관리번호</span>
          </label>
          <div>
            <input
              type="text"
              id="uid"
              name="_uid"
              placeholder="관리번호를 입력해 주세요."
              {...register("_uid", {
                onChange: onChangeHandle,
                pattern: {
                  value: /[0-9]/,
                  message: "숫자만 입력할 수 있습니다.",
                },
              })}
            />
          </div>
        </div>

        <div className="listSearchWrap">
          <div className="blockLabel">
            <span>회원권한</span>
          </div>
          <div>
            <input
              className="listSearchRadioInput"
              type="radio"
              id="userroleUser"
              name="_userrole"
              value="ROLE_USER"
              checked={searchData.userrole === "ROLE_USER"}
              {...register("_userrole", {
                onChange: onChangeHandle,
              })}
            />
            <label className="listSearchRadioLabel" htmlFor="userroleUser">
              일반
            </label>

            <input
              className="listSearchRadioInput"
              type="radio"
              id="userroleAdmin"
              name="_userrole"
              value="ROLE_USER,ROLE_ADMIN"
              checked={searchData.userrole === "ROLE_USER,ROLE_ADMIN"}
              {...register("_userrole", {
                onChange: onChangeHandle,
              })}
            />
            <label className="listSearchRadioLabel" htmlFor="userroleAdmin">
              관리자
            </label>
          </div>
        </div>

        <div className="listSearchWrap">
          <label className="blockLabel">
            <span>계약일</span>
          </label>
          <div>
            <input
              type="date"
              id="createTime"
              name="_createTime"
              {...register("_createTime", {
                onChange: onChangeHandle,
              })}
            />
          </div>
        </div>

        <div className="listSearchWrap">
          <div className="blockLabel">
            <span>계약관리</span>
          </div>
          <div>
            <input
              className="listSearchRadioInput"
              type="radio"
              id="useFlag1"
              name="_userUseFlag"
              value="1"
              checked={searchData.useFlag === "1"}
              {...register("_useFlag", {
                onChange: onChangeHandle,
              })}
            />
            <label className="listSearchRadioLabel" htmlFor="useFlag1">
              회원
            </label>

            <input
              className="listSearchRadioInput"
              type="radio"
              id="useFlag0"
              name="_useFlag"
              value="0"
              checked={searchData.useFlag === "0"}
              {...register("_useFlag", {
                onChange: onChangeHandle,
              })}
            />
            <label className="listSearchRadioLabel" htmlFor="useFlag0">
              해지
            </label>
          </div>
        </div>

        <div className="listSearchButtonWrap">
          <button type="reset" value="초기화" onClick={onResetHandle}>
            초기화
          </button>
          <button type="submit" value="검색">
            검색
          </button>
        </div>
      </form>
    </div>
  );
}

 

수정후

import { useEffect } from "react";
import { useForm } from "react-hook-form";

import { servicesPostData } from "../../Services/importData";
import { urlUserlist } from "../../Services/string";

export default function ComponentListUserSearch({
  setUserList,
  setListPage,
  searchClick,
  setSearchClick,
  page,
}) {
  // react-hook-form 라이브러리
  const { register, setValue, getValues, watch, reset, handleSubmit } =
    useForm();

  useEffect(() => {
    setValue("_userrole", "ROLE_USER");
    setValue("_useFlag", "1");
  }, []);

  useEffect(() => {
    // searchClick을 클릭한 (true) 상태에서 동작
    searchClick === true && SearchSubmit();
  }, [page.activePage || searchClick]);

  // 상위 컴포넌트에게 전달받은 useState의 set 함수
  // setUserList가 set으로 전달받은 후 사용하기 위해 && 사용
  const userList = (res) => {
    setUserList && setUserList(res);
  };
  const listPage = (res) => {
    setListPage && setListPage(res);
  };

  // submit 이벤트
  function SearchSubmit() {
    servicesPostData(urlUserlist, {
      offset: page.getPage,
      size: 15,
      uid: getValues("_uid"),
      userid: getValues("_userid"),
      name: getValues("_name"),
      userrole: getValues("_userrole"),
      mobile: getValues("_mobile"),
      createTime: getValues("_createTime"),
      isCid: getValues("_isCid"),
      useFlag: getValues("_useFlag"),
    }).then((res) => {
      if (res.status === "fail") {
        alert("검색하신 데이터가 없습니다.");
      }
      if (res.status === "success") {
        userList(res.data);
        listPage(res.page);
        setSearchClick(true);
      }
    });
  }

  // 초기화 이벤트
  function onResetHandle(e) {
    reset();
    setSearchClick(false);
    SearchSubmit();
  }
  // formWrap

  return (
    <div className="commonBox">
      <h3 className="blind">사업자관리 검색 필터</h3>
      <form
        className="listSearchForm formLayout"
        onSubmit={handleSubmit(SearchSubmit)}
      >
        <div className="listSearchWrap">
          <label className="blockLabel">
            <span>관리번호</span>
          </label>
          <div>
            <input
              type="text"
              id="uid"
              placeholder="관리번호를 입력해 주세요."
              {...register("_uid", {
                pattern: {
                  value: /[0-9]/,
                  message: "숫자만 입력할 수 있습니다.",
                },
              })}
            />
          </div>
        </div>
  

        <div className="listSearchWrap">
          <div className="blockLabel">
            <span>회원권한</span>
          </div>
          <div>
            <input
              className="listSearchRadioInput"
              type="radio"
              id="userroleUser"
              value="ROLE_USER"
              checked={watch("_userrole") == "ROLE_USER"}
              {...register("_userrole")}
            />
            <label className="listSearchRadioLabel" htmlFor="userroleUser">
              일반
            </label>

            <input
              className="listSearchRadioInput"
              type="radio"
              id="userroleAdmin"
              value="ROLE_USER,ROLE_ADMIN"
              checked={watch("_userrole") === "ROLE_USER,ROLE_ADMIN"}
              {...register("_userrole")}
            />
            <label className="listSearchRadioLabel" htmlFor="userroleAdmin">
              관리자
            </label>
          </div>
        </div>

 
        <div className="listSearchWrap">
          <label className="blockLabel">
            <span>계약일</span>
          </label>
          <div>
            <input
              type="date"
              id="createTime"
              {...register("_createTime", {
                onChange: (e) => {
                  const pickDate = new Date(e.target.value);
                  setValue("_createTime", pickDate.toISOString().slice(0, 19));
                },
              })}
            />
          </div>
        </div>

  

        <div className="listSearchWrap">
          <div className="blockLabel">
            <span>계약관리</span>
          </div>
          <div>
            <input
              className="listSearchRadioInput"
              type="radio"
              id="useFlag1"
              value="1"
              checked={watch("_useFlag") === "1"}
              {...register("_useFlag")}
            />
            <label className="listSearchRadioLabel" htmlFor="useFlag1">
              회원
            </label>

            <input
              className="listSearchRadioInput"
              type="radio"
              id="useFlag0"
              value="0"
              checked={watch("_useFlag") === "0"}
              {...register("_useFlag")}
            />
            <label className="listSearchRadioLabel" htmlFor="useFlag0">
              해지
            </label>
          </div>
        </div>

        <div className="listSearchButtonWrap">
          <button type="reset" value="초기화" onClick={onResetHandle}>
            초기화
          </button>
          <button type="submit" value="검색">
            검색
          </button>
        </div>
      </form>
    </div>
  );
}