오늘은 React Hook Form
라이브러리를 이용해서 MUI와 같은 UI 라이브러리 컴포넌트를 제어하는 방법을 같이 나눠보려고 합니다.
우선 React Hook Form
라이브러리에 대한 개념 없이 MUI 컴포넌트를 제어하는 방법을 찾기까지 너무 힘들었습니다.. 💩
대부분의 포스팅에서는 register
를 이용해서 input
과 같은 컴포넌트를 제어하는 방법을 알려주고 있었는데요.
MUI에 적용하기에는 맞지 않는 방법이었고, 제 문제에 대한 해답을 찾기 위해서 굉장히 많은 시간을 사용했습니다. 😢
제가 출처에 남긴 포스팅의 방식을 찾는데 오래 걸린 것은 아니지만.. 문제에 대한 해결 방법이라는 것을 아는데 오래 걸렸던 것 같습니다.. ㅎㅎ
Controller
사용하기React Hook Form
라이브러리에서 제공하는 컴포넌트로, 외부 컴포넌트를 React Hook Form
과 연결하여 제어하는데 사용됩니다.
Controller
기능name
, onChange
와 같은 속성을 외부 컴포넌트에 전달하여 필드의 상태를 업데이트 할 수 있습니다.rules
속성을 사용하여 필드에 대한 유효성 검사 규칙을 정의할 수 있습니다.import { useForm, Controller } from 'react-hook-form';
import { TextField } from '@mui/material';
const MyForm = () => {
const { control, handleSubmit } = useForm();
const onSubmit = (data) => console.log(data);
return (
<form onSubmit={handleSubmit(onSubmit)}>
<Controller
name='name'
control={control}
rules={{ required: true }}
render={({ filed }) => (
<TextField
label='name'
{...field}
/>
)}
</form>
)
}
render
속성에서 렌더링할 컴포넌트를 정의하고 field
속성을 전달해서 해당 컴포넌트 제어를 React Hook Form
에게 위임할 수 있습니다.
useController
사용하기이전에 사용한 Controller
컴포넌트와 동일한 권한을 가지는 커스텀 훅입니다.
추가적으로 Controller
와 동일한 props
와 method
를 사용할 수 있습니다.
useController
는 재사용 가능한 컴포넌트를 만드는데 유용합니다.
import { useForm, useController, UseControllerProps } from "react-hook-form";
type FormValues = { FirstName: string; };
function Input(props: UseControllerProps<FormValues>) {
const { field, fieldState } = useController(props);
return (
<div>
<input {...field} placeholder={props.name} />
</div>
);
};
export default function App() {
const { handleSubmit, control } = useForm<FormValues>({
defaultValues: {
FirstName: ""
},
mode: "onChange"
})
const onSubmit = (data: FormValues) => console.log(data)
return (
<form onSubmit={handleSubmit(onSubmit)}>
<Input control={control} name='FirstName' rules={{ required: true }} />
</form>
)
}
위의 코드와 비교했을 때, Controller
컴포넌트를 사용하지 않고, 별도로 정의한 Input 컴포넌트를 제어할 수 있습니다.
하지만 예제에서는 특정 타입에서만 사용할 수 있도록 되어 있기 때문에 이 부분을 제너릭을 활용해서 공통 컴포넌트로 사용할 수 있도록 바꿔줘야 합니다.
추가적으로 별도로 정의한 Input 컴포넌트에 MUI
컴포넌트인 textField
를 사용할 수 있지만, textfieldProps
를 제대로 넘겨주지 못할 것입니다.
useController + Mui Text Field
interface MuiProps {
textFieldProps?: TextFieldProps;
}
const Input = <
TFieldValues extends FieldValues = FieldValues,
TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>
>({
textFieldProps,
...props
}): MuiProps & UseController<TfieldValues, TName>) => {
const { field, fieldState: { error } } = useController(props)
return (
<TextField
{...textFieldProps}
{...field}
error={!!error}
helperText={!!error && error.message}
/>
)
}
const App = () => {
const { control, handleSubmit } useForm()
const onSubmit = (data) => console.log(data)
return (
<form onSubmit={handleSubmit(onSubmit)}>
<Input
name="FirstName"
control={control}
rules={{ required: true }}
textFIeldProps={{
label: "First Name"
}}
</form>
)
}
개인적으로 Controller
컴포넌트를 사용하는 것보다 깔끔하고 편하다고 생각합니다.
추가적으로, 마지막의 useController
와 MUI
의 textField
컴포넌트를 같이 사용하는 방식은 제가 참고한 포스팅에 훨씬 자세한 설명이 있으니 원문을 참고하시는 것도 좋을 것 같습니다! 🙏🙇♂️
만약 저처럼 React Hook Form
라이브러리와 MUI
를 같이 사용하고 싶다면, 결국 TextFieldProps
를 별도로 처리해줘야 합니다!
1. Controller
컴포넌트 활용하기
2. useController
훅 활용하기
가독성이나 선호도 차이에 따라 위의 2가지 방법 중 선택해서 사용하면 될 것 같습니다!
저의 경우에는 useController
훅을 활용해서 별도로 정의한 Input 컴포넌트를 만들었습니다!
개인적으로 이번 학습에 처음으로 챗 GPT를 많이 사용한 것 같습니다..
이전까지는 아직 챗 GPT를 믿을 수 없다는 생각이 많이 있어서 전혀 사용하지 않았는데요.
시간도 부족하고 막막한 느낌이 있어서 사용했더니.. 저에게 좋은 경험을 준 것 같습니다. 👍
그리고 React Hook Form
의 공식문서는 너무나도 잘 되어있다는 생각이 들었습니다.
처음부터 공식문서를 살펴 볼 시간이 있었다면.. 오히려 더 빠르게 해결 할 수 있지 않았을까? 라는 생각이 들었네요!
삼박자가 잘 맞는 아주 값진 경험이었습니다..⭐️