React-Hook-Form 심화 watch / useWatch / controll / register / wizard form

이지호_tech 2024. 4. 11. 12:55

저번 글에 이어서, 오늘은 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