리액트 훅 폼 (React Hook Form) 트러블 슈팅 1 - 여러 탭에 걸친 폼 데이터 제출하기(watch, setValue)
리액트 훅 폼 (React Hook Form) 트러블 슈팅 1 - 여러 탭에 걸친 폼 데이터 제출하기(watch, setValue 사용
위 슬라이드는 사용자가 알바 공고를 작성하기 위한 페이지이다.1. 모집 내용 2.모집 조건 3. 근무 조건 총 세가지 탭을 작성해서 전체 폼 데이터를 제출해야 한다. 🤔간단한 게시글이나 댓글
memoryelim.tistory.com
에 이어서 유형별 input 값 리액트 훅폼에 등록하는 방법 정리해보기!
유형별 input 값 리액트 훅폼에 등록하기
~~ input 타입 별로 정리해보자 ~~
1. Text
제일 간단히 구현했다.
register로 해당 필드를 리액트 훅폼에서 관리하도록 등록해주면
내부적으로 입력 필드의 이벤트(예: onChange, onBlur, onFocus 등)를 처리하며 상태를 자동으로 업데이트해주기 때문에
명시적으로 setValue를 사용할 필요가 없다.
그리고 register에는 ref도 포함이 되어있다.
ref를 통해 해당 DOM 요소를 React Hook Form의 상태와 연결하는데
ref는 prop으로 직접 전달할 수 없으므로
컴포넌트 내부에서 forwardRef로 감싸주어야 한다. (리액트 19부터는 불필요하다고 들었다)
2. Date
스웨거 상, 폼 제출 시 요구되는 날짜 형식은 UTC시간대이다. (toISOString)
UTC 시간대 : Date.toISOString()
` "workEndDate": "2024-12-06T12:38:50.412Z",`
보여주고 싶은 값 형식은 요러케.. => displayDate를 value로 전달한다.
Date.toLocaleString()
`"2024. 12. 6. 21:38:50"`
컴포넌트에서 관리할 값이 시작일과 종료일 두 개여서 register를 사용하지 않고 onChange 안에서 각각 setValue 해주었다.
그런데 이게 과연 좋은 방법일지 궁금해져서 지피티에게 물어보니
상태 관리가 복잡할 경우에는 사용해도 되지만 코드가 복잡해질 수 있고 성능최적화 이점을 얻을 수 없으니
리액트훅폼을 활용하는 방법으로 register와 Controller를 사용하는 방법을 각각 알려주었다
1. 첫번째 방법 : register + Controller
<DatePickerInput
startDate={startDate}
endDate={endDate}
{...register("startDate")}
{...register("endDate")}
onChange={handleRecruitmentDateChange}
/>
=> 이 경우에는 각각의 ref를 에 넘겨 주기 위해 시작일/종료일 input 이 각각 필요하다.
내 경우와 같이 데이터피커를 사용할때는 Controller를 함께 사용해야 한다.
<Controller
name="recruitmentDates"
control={control}
defaultValue={{ startDate: null, endDate: null }}
render={({ field }) => (
<DatePicker
selectsRange
startDate={startDate}
endDate={endDate}
onChange={handleDateChange}
isClearable
placeholderText="날짜 범위를 선택하세요"
{...field} // React Hook Form과 연결
/>
)}
/>
이게 더 복잡해지는거같은데,,?
2. 두번째 방법 : Controller
<Controller
name="recruitmentDates"
control={control}
render={({ field }) => (
<DatePickerInput
startDate={field.value?.startDate}
endDate={field.value?.endDate}
onChange={(dates) => field.onChange(dates)}
required={true}
displayValue={displayDate}
/>
)}
/>
이 경우가 훨씬 간단하다!
컴포넌트 내부에도 forwardRef만 감싸주면 된다 👍
react-datepicker UI 커스텀 관련 포스팅은 여긔
데이터 피커 (react-datepicker) 달력 UI 커스텀하기
react-datepicker 라이브러리의 달력 UI 커스텀 과정 기록입니다. 선택한 날짜 값과 관련된 로직은 제외하고 UI 컴포넌트 관련된 내용만 먼저 정리해보려고 한다. 대략적인 구조는 이렇게 구성했다.
memoryelim.tistory.com
3. Image file
날짜와 동일하게 onChange와 onDelete를 별도로 작성했다.
AWS S3에 업로드 해서 url를 반환 받도록 백엔드가 구성되어있어서
파일을 선택할때마다 onChange 핸들러에서 api 요청을 보내도록 했다.
이 부분을 구현하면서 했던 고민을 간단히 적어보겠다
바로 어제 이와 관련된 포스팅을 했는데 어째선지 실수로 삭제해서 ㅠ ㅜ
🤔 파일 업로드 api 요청 시점을 언제로 할 것인지 ?
1. 파일 선택 시 (onChange 핸들러에서)
2. 폼 제출 시 (onSubmit 핸들러에서)
✅ 처음엔 후자의 방법으로 구현했다.
사용자가 폼 제출 이전에 이미지를 추가했다가 삭제하고 다시 추가할 경우 불필요한 api 요청이 발생하는 것을 방지 하기 위해서였다.
💣 그러나 이 경우 불필요하게 관리해야 할 상태가 추가됐다.
File을 받아서 api 요청 -> 프리뷰는 blob url로 보여줌 & url 받아서 폼 데이터에 저장
=> File, url 두 개의 상태 관리
💣 임시저장할때도 이미지 업로드 api요청을 하다보니 중복된 로직이 발생했다.
(리액트쿼리를 사용해 중복 요청이 발생하진 않았을 것이다.)
💣 결정적으로 수정 페이지에서 에러가 발생했다.
작성 시 추가한 기존의 이미지와 새로 선택한 이미지 파일이 있을때
프리뷰를 보여주는 것 까지는 url로 보여주면 되지만
기존의 이미지는 파일을 가지고 있지 않고 url만 가지고 있기 때문에 이미지 업로드 api에 파일을 넘겨줄 수 없기 때문이다.
(파일은 제출할 폼데이터 필드에 포함되지 않으며, 불러올때도 이미지 url 데이터만 포함되어있다.)
⛏😂 따라서 이러한 문제들을 해결하기 위해 파일 선택 시 파일 업로드를 하는 것으로 수정을 했다.
4. Dropdown에서 값을 선택하는 경우
컴포넌트 내부에는 forwardRef로 감싸주고 input에 ref를 전달한다.
time 값을 선택하면 change event로 단언하여 이벤트를 onChange에 넘겨준다.
상위 컴포넌트에서 workStartTime값을 setValue하고 해당 값이 다시 value prop으로 컴포넌트에 내려가 시간이 표시된다.
5. 시급
보여주고 싶은 값 : 세자리 단위 콤마 = string
제출해야 하는 값 : 콤마 제거한 number
타입이 달라서 나름대로 이를 타개하기 위해 굉장히 코드가 길어졌었다
세자리 단위마다 콤마를 찍기 위해서 number.toLocaleString() 메서드를 사용하면 된다는 것을 몰랐던 나는,, 함수로 구현을 했었다 ㅋㅋㅋ
이렇게 알게됐으니 오히려 좋아^^ 다신 안잊을듯
유일하게 Controller를 사용한 필드인데, 멘토님이랑 같이 리팩토링 했었다 ㅎㅎ
value={value.toLocaleString()}
이렇게 작성해줌으로써 보여지는 값인 value 는 세자리 단위 콤마가 찍힌 채로 나오고
onChange={(e) => onChange(Number(e.target.value.replaceAll(",", "")))}
리액트 훅폼에 등록되는 값은 숫자값으로 전달이 된다
register와 달리 Controller를 사용할때는 filed에서 사용할 메서드를 명시적으로 적어주어야 한다.
또한 Controller가 자동으로 field.ref를 처리해주므로 ref를 전달할 필요가 없다고 한다 ~~
6. Boolean
간단 스바라시
checked 속성에 watch 값을 주어서 기존의 데이터가 있다면 그에 맞게 체크 표시되도록 함
마찬가지로 컴포넌트 내부에는 forwardRef 로 감싸주었고, ref를 전달했다.
필드 개수가 많아서 구현하는데 상당 기간이 소요됐는데 정리하고 보니 거의 비슷한 흐름으로 구현했었네
역시 직접 부딪혀보면서 리액트 훅폼을 많이 익혔다고 생각했는데 제대로 모르고 막 사용한것같다 ㅎㅎ..
그래도 전반적인 흐름을 알게되어 좋았고 다음에, 특히 라이브러리와 함께 쓸때는 Controller 사용도 적극적으로 해봐야겠다