사용자의 입력을 받고, 유효성 검사를 해야된다면 작성해야하는 코드량이 엄청 늘어난다.
import { useState, useEffect } from 'react';
export default function App() {
// 사용자가 입력한 값을 담을 state
const [email, setEmail] = useState('');
// input이 변경되면 값을 email에 저장
const onChangeEmail = (e) => {
setEmail(() => e.target.value);
};
// email 유효성 결과를 담을 state
const [emailValid, setEmailValid] = useState('');
// email 유효성 검사 함수
const emailRule = (v = '') => {
if (v === '') return;
if (!v.includes('@')) {
return '형식이 올바르지 않습니다.';
}
};
// email이 변경되면 유효성 검사
useEffect(() => {
if (!email) return;
setEmailValid(() => emailRule(email));
}, [email]);
return (
<div>
<div>
<label htmlFor='email'>이메일</label>
<input
id='email'
type='text'
value={email}
onChange={onChangeEmail}
placeholder='email@example.com'
/>
<p style={{ color: 'red' }}>{emailValid}</p>
</div>
<button>Sing up</button>
</div>
)
}
만약 여기서 비밀번호도 입력받으면 어떻게 될까? 말해 뭐해? 코드가 2배가 된다.
일단 iuput 태그를 컴포넌트로 만들자!
import React from "react";
const Input = ({
id = Math.floor(Math.random() * 10000 + 1), // random 아이디 부여
type = 'text',
value = '',
onChange,
label = '',
placeholder = '',
disabled = false,
valid = '', // 유효성 검사 결과
}) => {
return (
<div>
<div>
<label htmlFor={id}>{label}</label>
</div>
<input
id={id}
type={type}
value={value}
onChange={onChange}
placeholder={placeholder}
disabled={disabled}
/>
<p style={{color: 'red'}}>{valid}</p>
</div>
);
};
export default React.memo(Input);
React.memo
를 사용했는데, props가 변경되지 않으면 컴포넌트를 재렌더링 하지 않도록 최적화를 한 것이다.
이제 state와 유효성 검사 관련 로직을 hook으로 분리해보자.
import { useState, useEffect, useCallback } from 'react';
// 초기 값, 유효성 검사 함수를 받음
const useInput = (initValue = '', rule) => {
const [value, setValue] = useState(initValue);
const [valid, setValid] = useState('');
const onChange = useCallback((e) => {
setValue(() => e.target.value);
}, []);
// value가 변경되면 유효성 검사하기
useEffect(() => {
if (rule) {
setValid(() => rule(value));
}
}, [value]);
return {
value,
setValue,
onChange,
valid,
};
};
export default useInput;
이제 Input
컴포넌트와 useInput
hook을 사용해서 코드를 간결하게 작성해보자.
// Component
import Input from '../components/form/Input';
// hook
import useInput from '../hooks/useInput';
export default function App() {
const {
value: email,
valid: validEmail,
onChange: onChangeEmail,
} = useInput('', emailRule);
return (
<div>
<Input
label='이메일'
value={email}
onChange={onChangeEmail}
valid={validEmail}
placeholder='email@example.com'
/>
<button>Sing up</button>
</div>
);
}
const emailRule = (v = '') => {
if (v === '') return;
if (!v.includes('@')) {
return '형식이 올바르지 않습니다.';
}
};
input이 여러 개가 생겨도 코드가 2배로 늘어날 일은 없어졌다.