저번 글에 이어서, 오늘은 react-hook-form을 사용할때 놓칠수 있는 점들과, 여러가지 팁에 대해서 다뤄보겠습니다.
watch / useWatch
1. watch
watch는 useForm 훅에서 반환되는 함수 중 하나로, 지정된 폼 필드의 현재 값을 반환하여 줍니다. 폼 필드의 값 변화를 실시간으로 관찰 할 수 있으며, 특정 필드 또는 전체 폼의 값을 얻을 수 있습니다.
=> 주로 폼 컴포넌트 내에서 사용되며, 해당 컴포넌트의 리렌더링 범위에 영향을 줍니다.
즉, watch를 사용하는 컴포넌트는 관찰하는 필드 값이 변경 될 때마다 리렌더링 됩니다.
2. useWatch
useWatch는 리훅폼에서 제공하는 별도의 훅으로, watch와 유사하게 폼 필드의 값 변화를 관찰합니다. 하지만 useWatch는 특정 필드나 필드 그룹 변화에만 반응하고, 이를 통해 성능 최적화를 도모할 수 있습니다. 사용할때는 useForm의 컨텍스트를 직접 사용하거나, control 객체를 인자로 받아서 사용할 수 있습니다.
차이점 및 결론
watch는 폼 컴포넌트 전체의 리렌더링에 영향을 줄 수 있으며, 폼의 모든 필드 값을 관찰합니다.
반면, useWatch는 성능 최적화를 위해 특정 필드 값의 변화에만 반응하며, 불필요한 리렌더링을 줄이는데 도움이 됩니다.
폼 특정 부분에 조건부 논리에 대한 성능 최적화가 필요한 경우 => useWatch
폼 전체 상태를 간단히 관찰하고 싶을때, 리렌더링이 큰 문제가 되지 않는 경우=> watch
controller / register
Controller 사용 경우
<form onSubmit={handleSubmit(onSubmit)}>
{/* Controller 함수를 사용하여 입력 요소 등록 */}
<Controller
name="username"
control={control}
render={({ field }) => <input {...field} />}
/>
{/* 나머지 폼 요소들... */}
</form>
Register 사용 경우
<form onSubmit={handleSubmit(onSubmit)}>
{/* Register 함수를 사용하여 입력 요소 직접 등록 */}
<input {...register('username')} />
{/* 나머지 폼 요소들... */}
</form>
Controller
외부 라이브러리와의 연동이 간편하며, 컴포넌트 간의 상태를 쉽게 전달 할 수 있습니다.
import React from 'react';
import { useForm, Controller } from 'react-hook-form';
import { Select, MenuItem, FormControl, InputLabel } from '@mui/material';
function MyForm() {
const { control, handleSubmit } = useForm();
const onSubmit = data => {
console.log(data);
};
return (
<form onSubmit={handleSubmit(onSubmit)}>
<FormControl fullWidth>
<InputLabel id="demo-simple-select-label">Age</InputLabel>
<Controller
name="age"
control={control}
defaultValue=""
render={({ field }) => (
<Select
{...field}
labelId="demo-simple-select-label"
label="Age"
>
<MenuItem value={10}>Ten</MenuItem>
<MenuItem value={20}>Twenty</MenuItem>
<MenuItem value={30}>Thirty</MenuItem>
</Select>
)}
/>
</FormControl>
<input type="submit" />
</form>
);
}
해당 예시처럼 Controller 컴포넌트는 Select 컴포넌트의 value와 onChange를 react-hook-form의 field 객체와 연결합니다. 이를 통해 Select 컴포넌트의 선택된 값이 react-hook-form의 폼 상태 관리 시스템에 자동으로 통합되며, 폼 제출 시 해당 값이 함께 제출됩니다.
Register
직접적인 등록으로 렌더링 효율이 높습니다.
성능적으로 큰 규모의 폼이나 동적인 상황에서는 Controller의 렌더링 성능이 더 유리하지만, 작은 규모의 폼에서는 register가 직접 등록되어 성능이 더 우수합니다.
하지만 리훅폼에서는 공식적으로 Controller와 Register 을 선택할때, Controller를 권장합니다.
그 이유는
1. 일관성과 코드 통일화
2. 외부 라이브러리의 통합
3. 커스텀 컴포넌트의 모듈화
4. 프로젝트의 확장성
등의 더 많은 이점을 제공하기 때문입니다.
하지만 표준 HTML 폼필드와 직접 작업할때, 혹은 비교적 간단한 폼을 사용할때는 register을 사용하는것이 더 직관적일 수 있습니다.
Wizard Form
여러 단계를 거쳐 정보를 수집하는 폼
사용자가 다음 단계로 넘어가기 전에 현재 단계의 정보를 입력하고, 모든 단계를 완료한 후에 최종적으로 전체 데이터를 제출하는 형태의 폼 입니다.
보통 리훅폼과 상태관리 라이브러리를 같이 사용하는 것이 일반적입니다.
1. 상태관리 라이브러리를 사용하여 폼 데이터를 전역상태로 관리 합니다.
2. 각 단계를 별도의 컴포넌트로 구현하고, useForm을 사용하여 폼 관리를 합니다.
// StepOne.js
import React from 'react';
import { useForm } from 'react-hook-form';
import { useStateMachine } from 'little-state-machine';
import { updateAction } from './store';
function StepOne({ nextStep }) {
const { register, handleSubmit } = useForm();
const { actions } = useStateMachine({ updateAction });
const onSubmit = (data) => {
actions.updateAction(data);
nextStep();
};
return (
<form onSubmit={handleSubmit(onSubmit)}>
<input {...register("firstName")} placeholder="First Name" />
<input {...register("lastName")} placeholder="Last Name" />
<button type="submit">Next</button>
</form>
);
}
3. 사용자 입력을 전역 상태에 저장 합니다.
4. 모든 단계를 포함하는 메인 컴포넌트를 구성하고, 현재 단계에 따라 해당 컴포넌트를 렌더링 합니다.
// WizardForm.js
import React, { useState } from 'react';
import StepOne from './StepOne';
import StepTwo from './StepTwo'; // 가정: 두 번째 단계 컴포넌트
import { StateMachineProvider } from 'little-state-machine';
function WizardForm() {
const [step, setStep] = useState(1);
const nextStep = () => setStep(step + 1);
const prevStep = () => setStep(step - 1);
return (
<StateMachineProvider>
{step === 1 && <StepOne nextStep={nextStep} />}
{step === 2 && <StepTwo nextStep={nextStep} prevStep={prevStep} />}
// 추가 단계...
</StateMachineProvider>
);
}
5. 각 단계에서 처리되어 전역 상태에 저장된 모든데이터를 최종적으로 처리하거나 서버로 제출 합니다.
// StepFinal.js
import React from 'react';
import { useForm } from 'react-hook-form';
import { useStateMachine } from 'little-state-machine';
import { updateAction } from './store';
function StepFinal() {
const { handleSubmit } = useForm();
const { state, actions } = useStateMachine({ updateAction });
const onSubmit = async (data) => {
// 마지막 단계의 데이터를 상태에 추가
actions.updateAction(data);
// 전체 폼 데이터를 서버로 전송
await fetch('YOUR_ENDPOINT', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(state.formData),
});
// 제출 후 처리, 예: 성공 메시지 표시, 페이지 이동 등
};
return (
<form onSubmit={handleSubmit(onSubmit)}>
{/* 마지막 단계의 폼 필드들 */}
<button type="submit">Submit</button>
</form>
);
}
사용자에게 한 번에 하나의 섹션 또는 단계만 보여주어, 폼 작성 과정을 더 관리하기 쉽고 이해하기 쉽게 만들어주는 좋은 기능입니다.
'웹' 카테고리의 다른 글
Babel 이란? +babel-plugin-macros (1) | 2024.04.19 |
---|---|
Javascript 의 객체 리터럴 / 관련 팁 들 (0) | 2024.04.16 |
React Hook Form 훑어보기 (0) | 2024.04.08 |
CRA , VITE 에서 .env 파일 사용하기 (0) | 2023.03.23 |
Vite 첫 사용 후기 (0) | 2023.03.20 |