<Input name='user-id' value={id} required onChange={onChangeId} />
컴포넌트에서 props로 넘겨주는 함수(위의 예제에서 onChangeId)는 useCallback이 필수적이다.
자식 컴포넌트로 넘겨주는 함수는 무조건 useCallback으로 감싸준다.
useCallback(fn, deps)
함수(fn) 내부에서 쓰는 state를 deps 배열로 넣는다.
const memoizedCallback = useCallback(
() => {
doSomething(a, b);
},
[a, b],
);
배열 내에 여러 개의 값이 있다면 그중의 단 하나만 다를지라도 리액트는 effect를 재실행합니다.
이것이 의미하는 바는 다음과 같습니다. [count]를 useEffect
, useCallback
등의 두 번째 인수로 넣었다고 가정해 봅시다.
만약 count가 5이고 컴포넌트가 리렌더링된 이후에도 여전히 count는 변함없이 5라면 리액트는 이전 렌더링 시의 값 [5]를 그다음 렌더링 때의 [5]와 비교합니다. 배열 내의 모든 값이 같기 때문에(5 === 5) 리액트는 effect를 건너뛰게 됩니다.
count가 6으로 업데이트된 뒤에 렌더링하면 리액트는 이전에 렌더링된 값 [5]를 그다음 렌더링 시의 [6]와 비교합니다. 이때 5 !== 6 이기 때문에 리액트는 effect를 재실행합니다. 이를 활용하면 최적화가 가능합니다.
또한 특정 state가 특정한 condition 일 때만 callback(또는 effect)를 재실행하고 싶다면 deps 배열의 state에 특정 조건을 넣는 것도 가능합니다.
useEffect(() => {
document.title = `You clicked ${count} times`;
}, [count === 5]); // count가 5일 때만 effect를 재실행합니다.
const onChangePasswordChk = useCallback(
e => {
setPasswordError(e.target.value !== password);
setPasswordCheck(e.target.value);
},
[password],
);
const LoginForm = () => {
const [id, , onChangeId] = useInput('');
const [password, , onChangePassword] = useInput('');
const onSubmitForm = useCallback(() => {
// 자식 컴포넌트(LoginForm 컴포넌트에서는 Form, Input 등이 자식컴포넌트)
// 넘겨주는 함수는 무조건 useCallback으로 감싸준다.
console.log({
id,
password,
});
}, [id, password]);
return (
<Form onFinish={onSubmitForm}>
<div>
<label htmlFor='user-id'>아이디</label>
<br />
<Input name='user-id' value={id} onChange={onChangeId} required />
</div>
<div>
<label htmlFor='user-password'>비밀번호</label>
<br />
<Input.Password
name='user-password'
value={password}
onChange={onChangePassword}
required
/>
</div>
<div>
<Button type='primary' htmlType='submit' loading={false}>
로그인
</Button>
<Link href='/signup'>
<a>회원가입</a>
</Link>
</div>
</Form>
);
};
export default LoginForm;