훅이란?
- 리액트에는 컴포넌트와 그 안의 state라는 것이 존재한다.
- 컴포넌트의 종류에는 2가지가 존재한다 : 클래스형, 함수형
- 클래스형의 경우, state를 설정하는 것이 편했다.
- 반면 함수형의 경우 직접 state를 설정하는 것이 불가능하다.
- 따라서 이를 위해서 등장한 것이 바로 훅이다.
- 대표적인 훅의 종류를 살펴보자.
useState
변수에 state를 걸어주는 함수
버튼을 누르면 그 값이 계속 올라게끔 리액트 코드를 짜보자.
javascript
import React, { useState } from "react";
function Counter(props) {
var count = 0;
return (
<div>
<p>총 {count}번 클릭했습니다.</p>
<button onClick={() => setCount(count + 1)}>
클릭
</button>
</div>
)
}
export default Counter;
그런데, 이렇게 짜면 도저히 텍스트(”총 ..번 클릭했습니다”)가 바뀌지 않는다.
왜? count 값이 바뀌어도, 화면을 다시 그리지 않기(= 리렌더링이 안되기) 때문이다
따라서 state를 사용해서 바꿔줘야 한다.
javascript
import React, { useState } from "react";
function Counter(props) {
const [count, setCount] = useState(0);
return (
<div>
<p>총 {count}번 클릭했습니다.</p>
<button onClick={() => setCount(count + 1)}>
클릭
</button>
</div>
)
}
export default Counter;
인제 값이 바뀌는 것을 눈치챌 수 있다.
기본 공식
어떠한 변수에 state를 걸고 싶다면, 다음과 같이 설정하면 된다.
javascript
// 변수명 = num, 초깃값 = 0이라고 하면...
const [num, setnum] = useState(0)
useEffect
componentDidMount(), componentDidUpdate(), componentWillUnmount()를 묶어서 만들어낸 함수라고 생각하면 된다.
언제 쓰는가?
- 데이터 가져오기 (Fetching data): 컴포넌트가 처음 마운트될 때 서버로부터 데이터를 가져오는 작업을 수행.
- 구독 설정 및 해제 (Subscribing and unsubscribing): 웹소켓, 데이터 스트림, 이벤트 리스너 등 구독을 설정하고 컴포넌트가 언마운트될 때 구독을 해제.
- DOM 조작: 컴포넌트가 렌더링될 때 또는 업데이트될 때 직접 DOM을 조작하는 작업이 필요할 때 사용.
- 타이머 설정 (Timers): 타이머나 인터벌을 설정하고 해제.
왜 쓰는가?
- 코드 구조 개선: 흩어져 있는 생명주기 메서드(componentDidMount, componentDidUpdate, componentWillUnmount)를 하나로 모아 가독성을 높임.
- 명시적 의존성 관리: useEffect의 두 번째 인수로 의존성 배열을 전달하여, 어떤 값이 변경될 때 효과를 다시 실행할지 명시적으로 관리.
- 클린업 기능: 정리 함수(clean-up function)를 반환하여 컴포넌트가 언마운트될 때 또는 의존성이 변경될 때 실행할 정리 작업을 지정.
예제 코드
javascript
import React, { useState, useEffect } from "react";
function Counter(props) {
const [count, setCount] = useState(0);
useEffect(() => {
console.log(`You clicked ${count} times`);
return () => {
console.log('Cleanup on component unmount');
console.log('or before next effect runs');
};
}, [count]);
return (
<div>
<p>총 {count}번 클릭했습니다.</p>
<button onClick={() => setCount(count + 1)}>
클릭
</button>
</div>
)
}
export default Counter;
- useEffect() 속의 코드들을 보자
console.log():componentDidMount,componentDidUpdate의 기능을 하고 있다.return():componentWillUnmount의 기능을 하고 있다.
useMemo
성능을 최적화한 useEffect()
언제 쓰나?
- 비용이 많이 드는 계산 : 배열의 정렬, 필터링, 맵핑 등 비용이 많이 드는 계산을 수행해야 할 때.
- 의존성 변경 시에만 재계산 : 특정 상태나 프롭이 변경될 때만 재계산이 필요할 때.
- 렌더링 최적화 : 동일한 결과를 반복적으로 계산하지 않고, 메모이제이션된 값을 사용하여 렌더링 성능을 최적화할 때.
조심할 점
- useMemo()로 전달된 함수는 렌더링이 일어나는 동안 실행.
- 따라서 렌더링이 일어나는 동안 실행되면 안되는 작업은 useEffect()를 사용
- ex. 서버에서 데이터 받아오기, 수동으로 DOM 변경.
javascript
import React, { useState, useMemo } from 'react';
function XXX_Component() {
const [count, setCount] = useState(0);
const [otherValue, setOtherValue] = useState(0);
// 비용이 많이 드는 계산을 useMemo로 최적화
const expensiveResult = useMemo(() => {
let result = 0;
for (let i = 0; i < 1000000000; i++) {
result += i;
}
return result + count;
}, [count]); // count가 변경될 때만 재계산됨
return (...);
}
export default XXX_Component;