useCallback
useMemo()와 비슷하나, 값 대신 함수를 반환
언제 쓰나?
- 자식 컴포넌트에 콜백을 전달할 때 : 자식 컴포넌트가 React.memo나 shouldComponentUpdate를 사용하여 최적화된 경우, 콜백 함수가 변경되지 않도록 해서 불필요한 리렌더링을 방지할 수 있습니다.
- 성능 최적화가 필요할 때 : 함수 재생성이 비싼 연산이거나, 함수가 종속된 값이 자주 변경되지 않는 경우
- 의존성 배열에 기반한 재생성 : 특정 값이 변경될 때만 함수를 재생성하고 싶을 때 유용
예시 코드
javascript
import React, { useState, useCallback } from 'react';
function ParentComponent() {
const [count, setCount] = useState(0);
const [otherValue, setOtherValue] = useState(0);
// handleClick 함수는 count가 변경될 때만 재생성됨
const handleClick = useCallback(() => {
console.log(`Count: ${count}`);
}, [count]);
return (
<div>
<p>Count: {count}</p>
<p>Other Value: {otherValue}</p>
<button onClick={() => setCount(count + 1)}>
Increment Count
</button>
<button onClick={() => setOtherValue(otherValue + 1)}>
Increment Other Value
</button>
<ChildComponent onClick={handleClick} />
</div>
);
}
function ChildComponent({ onClick }) {
console.log('Child component rendered');
return (
<div>
<button onClick={onClick}>Click me</button>
</div>
);
}
export default ParentComponent;
좀 복잡한 코드이지만, 한번 잘 살펴보자.
- count가 변경될 때 useCallback()은 handleClick() 함수를 생성
- handleClick()은 ChildComponent의 props인 onClick으로 들어가고 있음
- 따라서
Click me를 누를 때, handleClick()이 실행.
즉, ParentComponent가 리렌더링 된다고 ChildComponent가 무조건 리렌더링 되지 않음.
따라서 성능상의 이점을 얻을 수 있음.
useRef
레퍼런스를 사용하기 위한 훅
레퍼런스 : 특정 객체나 요소를 참조하는 것. (객체지향에서의 그 레퍼런스처럼 생각)
예시 코드
javascript
import React, { useEffect, useRef } from 'react';
function LoginForm() {
const emailInput = useRef(null);
useEffect(() => {
emailInput.current.focus();
}, []);
return (
<form>
<label>
Email:
<input ref={emailInput} type="email" />
</label>
<label>
Password:
<input type="password" />
</label>
<button type="submit">Login</button>
</form>
);
}
export default LoginForm;
-
useRef훅을 호출하여emailInput이라는 변수를 생성한다. 이 변수는 초기 값으로 null을 가진 ref 객체를 반환한다.이 ref 객체는
.current를 가지고 있으며, 이는 나중에 참조할 DOM 요소를 가리킴. -
input요소에ref 속성을 설정하여, 이 요소가emailInput객체의.current속성에 할당되도록 한다. -
useEffect()가 걸리면,emailInput.current.focus()가 자동으로 실행.사용자가 로그인 폼 페이지에 접속하면 이메일 입력 필드가 자동으로 입력을 받을 수 있게 준비되어 있다.
훅의 규칙
- 무조건 최상위 레벨에서만 호출해야 한다.
- 즉 반복문, 중첩문, 조건문 안에서는 실행하면 안된다.
- 컴포넌트가 렌더링 될 때마다 매번 같은 순서로 호출되어야 한다.
- 리액트 함수 컴포넌트에서만 훅을 호출해야 한다.
- 일반적인 자바스크립트 함수에서 훅을 호출하면 안된다.
사용자 정의 훅
- 특정 로직을 컴포넌트에서 분리하여 독립적으로 테스트하고 재사용할 수 있다.
- 이를 통해 코드의 가독성과 유지보수성을 크게 향상시킬 수 있다.
- 규칙 : 반드시 use로 시작해야 한다.
ex. useWindowSize, useTimer
예시 코드
javascript
import { useState, useEffect } from 'react';
// 커스텀 훅: useWindowSize
function useWindowSize() {
// width와 height를 상태로 정의
const [size, setSize] = useState({
width: window.innerWidth,
height: window.innerHeight,
});
// 윈도우 크기 변경을 처리하는 핸들러
const handleResize = () => {
setSize({
width: window.innerWidth,
height: window.innerHeight,
});
};
useEffect(() => {
// 컴포넌트가 마운트되었을 때 리스너 등록
window.addEventListener('resize', handleResize);
// 클린업 함수: 컴포넌트가 언마운트되거나 리스너가 변경될 때 리스너 제거
return () => {
window.removeEventListener('resize', handleResize);
};
}, []); // 빈 배열을 두 번째 인자로 주어, 한 번만 실행되도록 설정
return size;
}
export default useWindowSize;
- 커스텀 훅 정의
- useWindowSize 훅은 useState와 useEffect를 사용하여 윈도우의 크기를 추적.
- useState를 사용하여 size 상태를 정의하고, 초기값으로 현재 창의 크기를 설정.
- handleResize 함수는 창 크기가 변경될 때 상태를 업데이트.
- useEffect는 컴포넌트가 마운트될 때 창 크기 변경 이벤트 리스너를 등록하고, 컴포넌트가 언마운트될 때 리스너를 제거하는 클린업 함수를 반환.
javascript
import React from 'react';
import useWindowSize from './useWindowSize';
function WindowSizeComponent() {
const size = useWindowSize(); // 커스텀 훅 호출
return (
<div>
<h1>Window Size</h1>
<p>Width: {size.width}px</p>
<p>Height: {size.height}px</p>
</div>
);
}
export default WindowSizeComponent;
- 커스텀 훅 사용:
- WindowSizeComponent 컴포넌트는 useWindowSize 훅을 호출하여 현재 창의 크기를 가져온다.
- size 객체는 width와 height 속성을 가지며, 이를 JSX에서 출력하여 현재 창 크기를 표시.