useEffect의 의존성 배열
- useEffect에서 의존성 배열을 다루는 것은 굉장히 중요한 부분
- 의존성 배열을 제대로 다루지 못한다면 useEffect를 사용하면서 버그가 발생할 확률이 굉장히 높아진다.
1. 의존성 배열이란?
의존성 배열이란 얕게 이해하자면
- useEffect에 두번째 인자로 넘기는 배열
- 그리고 두번째 인자를 넘기지 않으면 Effect는 매번 실행
- 빈 배열을 넘긴다면 컴포넌트의 첫번째 렌더링 이후에만 실행
이렇게만 이해하고 그동안 useEffect를 사용하고 있었다면 애플리케이션에서 버그가 발생할 확률이 굉장히 높다.
useEffect의 시그니쳐는 아래와 같다.
useEffect(effect, 의존성)
여기서 effect는 함수의 형태로 표현되고, 의존성은 여러 의존성들을 한번에 전달하기 위해서 배열의 형태로 표현
의존성이란 말을 해석해보면
- A라는 요소가 온전히 동작하기 위해서 B, C, D 등 다른 요소들을 필요로 할 때 A는 B, C, D에 의존하고 있다고 표현
- 그리고 “B, C, D는 A의 의존성이다” 라고도 표현할 수 있다.
그렇다면 useEffect에서 의존성 배열이란 “무언가가 의존하고 있는 요소들의 모음” 이라고 할 수 있다. 그리고 여기서 말하는 무언가는 바로 effect 함수다. 즉, useEffect의 의존성 배열은 “effect 함수가 의존하고 있는 요소들의 모음” 이라고 할 수 있다.
“의존하고 있다” 라는 말은 쉽게 설명해 단순히 effect 함수가 사용하고 있는 외부의 변수들이 의존성이다.
function Component(){
const [count, setCount] = useState(0);
useEffect(()=>{
document.title = `you clikced ${count} times`
}, [count]];
}
위의 예시에서 effect 함수는 count 라는 외부의 변수를 내부에서 사용하고 있다. 따라서 effect 함수의 의존성은 count 이며 count를 의존성 배열에 넣어줘야한다.
이렇게 되면 useEffect는 리렌더링이 된 후 의존성 배열을 검사해서 의존성에 있는 값들이 변경되었을 경우에 다시 새로운 의존성을 가지고 effect를 실행시켜 준다.
2. useEffect 의존성 배열의 잘못된 활용
useEffect에서 의존성 배열은 핵심이 되는 부분인데, 이런 의존성 배열을 잘 못 설정하고 활용하는 경우가 있다.
필요 없는 요소들을 굳이 의존성 배열에 넣는 실수는 잘 하지 않지만, 필요한 의존성을 제대로 의존성 배열에 넣어주지 않는 실수를 많이 한다.
function Component(){
const [count, setCount] = useState(0);
useEffect(()=>{
document.title = `you clikced ${count} times`
}, []];
}
위 코드의 문제점은 count가 변경되었을 때 document.title이 맞춰서 변경되지 않는다는 것이다. 왜냐하면 의존성 배열에 의존성을 제대로 넣어주지 않았기 때문이다.
3. useEffect 의존성 배열을 잘 설정하는 법
useEffect에서 버그가 발생하지 않게 의존성 배열을 잘 설정하는 방법은 아래의 원칙을 지키는 것이다.
- “모든 의존성을 빼먹지 말고 의존성 배열에 명시하기”
여기에 아래의 원칙을 추가해주는 것
- “가능하다면 의존성을 적게 만들기”
- “가능하다면 의존성을 적게 만들기”
// -------------------------------- Bad --------------------------------
function App() {
const [count, setCount] = useState(0);
useEffect(() => {
const id = setInterval(() => {
setCount(count + 1);
}, 1000);
return () => clearInterval(id);
}, []);
return (
<div>
<h1>count:{count}</h1>
</div>
);
}
// -------------------------------- Good --------------------------------
function App() {
const [count, setCount] = useState(0);
useEffect(() => {
const id = setInterval(() => {
setCount(prevCount => prevCount + 1);
}, 1000);
return () => clearInterval(id);
}, []);
return (
<div>
<h1>count:{count}</h1>
</div>
);
}
“모든 의존성을 빼먹지 말고 의존성 배열에 명시하기”
// -------------------------------- Bad --------------------------------
function Component(){
const [count, setCount] = useState(0);
useEffect(()=>{
document.title = `you clikced ${count} times`
}, []];
}
// -------------------------------- Good --------------------------------
function Component(){
const [count, setCount] = useState(0);
useEffect(()=>{
document.title = `you clikced ${count} times`
}, [count]];
}
간단한 일반 변수, state, props의 경우에는 의존성을 빼먹지 않고 의존성 배열에 명시하기가 쉽다.
하지만, 함수 컴포넌트의 내부에서 선언한 Object, Function의 경우에는 매번 함수 컴포넌트의호출 때 마다 새로운 객체, 함수가 선언되고 참조형 데이터 타입의 특징으로 인해 객체 내부의 요소들이 동일하더라도 새롭게 생성된 객체와 이전 객체를 비교하면 서로 다른 객체라고 판단되게 된다.
그래서 아래의 코드는 무한 루프를 반복한다.
function Component(){
const [count, setCount] = useState(0);
const increaseCount = () => {
setCount(prev => prev + 1);
}
useEffect(increaseCount, [increaseCount]];
}
위의 문제를 해결하기 위해서 여러가지 방안을 시도해볼 수 있다.
1. 의존성을 제거하기 ⇒ 함수를 effect 안에 선언하기
function Component(){
const [count, setCount] = useState(0);
useEffect(() => {
const increaseCount = () => {
setCount(prev => prev + 1);
};
increaseCount();
}, []];
}
2.함수를 컴포넌트 바깥으로 이동시키기
function Component(){
const getUserAuth = () => {
localStorage.getItem("ACCESS_TOKEN");
}
useEffect(() => {
getUserAuth();
}, []];
}
function Component(){
useEffect(() => {
getUserAuth();
}, [getUserAuth]];
}
const getUserAuth = () => {
localStorage.getItem("ACCESS_TOKEN");
}
3. 메모이제이션
function Component(){
const [count, setCount] = useState(0);
const increaseCount = () => {
setCount(prev => prev + 1);
}
useEffect(() => {
// do something 1
increaseCount();
}, []];
useEffect(() => {
// do something 2
increaseCount();
}, []];
}
function Component(){
const [count, setCount] = useState(0);
const increaseCount = useCallback(() => {
setCount(prev => prev + 1);
}, []);
useEffect(() => {
// do something 1
increaseCount();
}, [increaseCount]];
useEffect(() => {
// do something 2
increaseCount();
}, [increaseCount]];
}
참고자료
'Library & Framework > React.js' 카테고리의 다른 글
관심사의 분리 & 리액트에서 관심사를 분리하는 법 (0) | 2022.10.24 |
---|---|
Context API 개념과 사용법 (0) | 2022.10.21 |
React 렌더링 최적화 & Advanced Hook (0) | 2022.10.19 |
클래스형 컴포넌트 vs 함수형 컴포넌트 (0) | 2022.03.28 |