250x250
Notice
Recent Posts
Recent Comments
Link
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | ||||||
2 | 3 | 4 | 5 | 6 | 7 | 8 |
9 | 10 | 11 | 12 | 13 | 14 | 15 |
16 | 17 | 18 | 19 | 20 | 21 | 22 |
23 | 24 | 25 | 26 | 27 | 28 |
Tags
- useEffect
- 리액트 공식문서
- 파이썬 enumerate
- 파이썬 반복문
- 타입스크립트
- 내일배움캠프
- Next 팀 프로젝트
- tanstack query
- 내일배움캠프 프로젝트
- 한글 공부 사이트
- 코딩테스트
- 타입스크립트 리액트
- 파이썬 slice
- useState
- 파이썬 for
- 파이썬 replace
- 내일배움캠프 최종 프로젝트
- 프로그래머스
- 파이썬 딕셔너리
- 리액트 프로젝트
- REACT
- 타입스크립트 props
- 파이썬 for in
- typeScript
- JavaScript
- 자바스크립트
- React Hooks
- 내배캠 프로젝트
- 리액트 훅
- 리액트
Archives
- Today
- Total
sohyeon kim
[React] useState & useRef & react-hook-form : 제어/비제어 컴포넌트, useRef 단점, 언제 무엇을 써야 하는가 본문
React
[React] useState & useRef & react-hook-form : 제어/비제어 컴포넌트, useRef 단점, 언제 무엇을 써야 하는가
aotoyae 2024. 8. 13. 21:49728x90
💡제어 컴포넌트 useState 와 비제어 컴포넌트 useRef 를 비교해보자!
제어 컴포넌트
- React 에 의해 값이 제어되는 입력 폼 엘리먼트
- 사용자의 입력을 기반으로 state 를 관리하고 업데이트한다.
- 모든 input 이 변경될 때 마다 리렌더링
import React, { useState } from 'react';
function MyInput() {
const [inputValue, setInputValue] = useState(null);
const handleChange = (e) => {
setInputValue(e.target.value);
};
return <input onChange={(e) => handleChange(e)} value={inputValue} />;
}
비제어 컴포넌트
- React 에서 값이 제어되지 않는 컴포넌트
- 값이 변화되더라도 input 을 제외한 필드가 리렌더링 되지 않음
import React, { useRef } from 'react';
function Test() {
return <MyInput />;
}
function MyInput() {
const inputNode = useRef(null);
return <input ref={inputNode} />;
}
export default MyInput;
기능
|
제어 컴포넌트 | 비제어 컴포넌트 |
일회성 정보 검색 (예 : 제출) | ⭕️ | ⭕️ |
제출 시 값 검증 | ⭕️ | ⭕️ |
실시간으로 필드 값 유효성 검사 | ⭕️ | ❌ |
조건부로 제출 버튼 비활성화(disabled) | ⭕️ | ❌ |
실시간으로 입력 형식 적용 (숫자만 가능하게 등) | ⭕️ | ❌ |
동적 입력 | ⭕️ | ❌ |
즉각적으로 값에 대한 피드백이 필요하다면
➡️ 제어 컴포넌트 사용
즉각적인 피드백이 불필요하고 제출 시에만 값이 필요하다면, 불필요한 렌더링과 값 동기화가 싫다면
➡️ 비제어 컴포넌트 사용
🥸 프로젝트 중 했던 고민
회원가입 form 을 만들기 전 처음엔 useRef 를 사용하기로 결정했다.
값을 입력할 때마다 렌더링을 일으키고 싶지 않았고, 유효성 검사를 onBlur 시에만 실행하고 싶었기 때문이다.
const handleBlur = (ref: RefObject<HTMLInputElement>) => {
const refValue = ref.current?.value;
if (refValue) {
const ID_REGEX = /^(?=.*[a-z])[a-z0-9-_]{5,20}$/; // 5~20자, 소문자 하나는 반드시 포함, 숫자/-/_ 는 선택적
const PW_REGEX =
/^(?=.*[a-z])(?=.*\d)(?=.*[@$!%*?&_\-])[A-Za-z\d@$!%*?&_\-]{8,16}$/; // 8~16자, 소문자, 숫자, 특수문자 반드시 포함, 대문자는 선택적
if (ref === idRef) {
setId(refValue);
isValidValue(refValue, ID_REGEX, true);
} else if (ref === pwRef) {
setPw(refValue);
isValidValue(refValue, PW_REGEX, false);
} else {
setPwCheck(refValue);
pw === pwCheck ? setPwCheckIsValid(true) : setPwCheckIsValid(false);
}
}
};
// ...
<input
type="text"
placeholder="아이디"
ref={idRef}
onBlur={() => handleBlur(idRef)}
/>
하지만 진행하면서 화면에 보이는 값과 input 의 상태 값이 같아야 한다는 생각이 강해지면서 결국 useState 로 변경하였다.
// 닉네임 유효성 조건
const NICKNAME_REGEX = /^(?=.*[a-z가-힣])[a-z가-힣0-9]{1,20}$/; // 1~20자, 한글이나 소문자 하나는 반드시 포함, 숫자는 선택적
// 타입에 따라 상태 설정
switch (type) {
case "id":
setId(value);
setIdIsEnterd(isEntered);
handleValidate(value, ID_REGEX, setIdIsValid);
break;
case "nickname":
setNickname(value);
setNicknameIsEnterd(isEntered);
handleValidate(value, NICKNAME_REGEX, setNicknameIsValid);
break;
case "pw":
setPw(value);
setPwIsEnterd(isEntered);
handleValidate(value, PW_REGEX, setPwIsValid);
break;
case "pwCheck":
setPwCheck(value);
setPwCheckIsEnterd(isEntered);
break;
default:
break;
}
};
const handleValidate = (
value: string,
regex: RegExp,
setValidity: React.Dispatch<React.SetStateAction<boolean>>,
) => {
setValidity(regex.test(value)); // 값별로 유효한지 체크
};
그러던 중.. react-hook-form 을 발견했다.
👀 React Hook Form : 빠른 성능과 간편한 사용으로 폼 관리
- form 을 효율적으로 관리, 검증하기 위한 비제어 컴포넌트 기반의 라이브러리
- 상태를 직접 관리하지 않기 때문에 리렌더링을 줄이고 성능을 향상시킨다.
- 높은 유연성으로 다양한 검증 라이브러리와 통합이 가능하다.
npm install react-hook-form or yarn add react-hook-form
😌 내가 느낀 장점
유효성 검사가 정말 간편해졌다.
또한, mode 변경으로 onChange 시 검사를 할 건지 onBlur 시 검사를 할 건지 설정할 수 있다.
input 이 많을 수록 더 편리할 것 같다.
"use client";
import { FieldValues, useForm } from "react-hook-form";
import { collection, addDoc } from "firebase/firestore/lite";
import { db } from "@/shared/firebase";
const AdminPage = () => {
const usersCollection = collection(db, "users");
const { register, handleSubmit, formState } = useForm({
mode: "onChange",
defaultValues: { // 초기값
name: "",
email: "",
password: "",
phone: "",
title: "",
specialization: "",
assigned_leads: "",
},
});
const onSubmit = async (value: FieldValues) => {
const newUser = {
...value,
role: "sherpa",
};
try {
const res = await addDoc(usersCollection, newUser);
console.log(res);
} catch (error) {
console.log(error);
}
};
<form
onSubmit={handleSubmit(onSubmit)}
className="flex flex-col gap-6"
>
<div className="grid grid-cols-4 gap-4">
<div className="flex flex-col gap-2">
<label htmlFor="name" className="w-64 text-left">
Name
</label>
<input
{...register("name", {
required: {
value: true,
message: "필수 정보입니다.",
},
pattern: {
value: /^[가-힣a-zA-Z]{2,20}$/, // 2~20자, 한글, 영어만 허용
message: "2~20자의 한글이나 영어로 작성해 주세요.",
},
})}
type="text"
placeholder="이름"
className="w-64 rounded-lg bg-zinc-600 p-2"
/>
{formState.errors.name && (
<span className="text-sm text-red-600">
{formState.errors.name.message}
</span>
)}
</div>
// ...
<button
disabled={!formState.isValid}
type="submit"
className="w-full self-end rounded-lg bg-zinc-700 p-4"
>
추가하기
</button>
</form>
🔗 https://velog.io/@hyunjine/useState-vs-useRef
728x90
반응형
'React' 카테고리의 다른 글
[React] 리액트 공식문서 정리 1 : UI 표현하기 - 불변성, JSX, key, 컴포넌트, 순수 함수, 트리 (14) | 2024.10.16 |
---|---|
[React] Context 외부에서 데이터를 사용하려 할 때의 에러 처리, useContext (0) | 2024.10.11 |
[React] Zustand : 너무나 가벼운 상태관리 라이브러리 (0) | 2024.08.07 |
[React] TanStack Query : Infinite Queries & Intersection Observer 무한스크롤 활용 (0) | 2024.08.07 |
[React] TanStack Query : Paginated & Lagged Queries 페이지네이션 & 지연 쿼리 (0) | 2024.08.07 |