의존성 배열에는 state 값을 넣고 state 값이 바뀔 때마다 useEffect가 실행되는 식으로 활용하는 줄만 알았는데 boolean 값으로 평가되는 비교식도 들어갈 수 있다는 걸 알게 됐다.
아래는 getWinNumbers()
로 총 7개의 로또 번호를 뽑아 배열 winNumbers
에 담아두고 1초에 하나씩 배열 winBalls
에 담는 컴포넌트이다.
function App() {
const lottoNumbers = useMemo(() => getWinNumbers(), []);
const [winNumbers, setWinNumbers] = useState(lottoNumbers);
const [winBalls, setWinBalls] = useState([]);
const [bonus, setBonus] = useState(null);
const [redo, setRedo] = useState(false);
const timeouts = useRef([]);
console.log("winBalls", winBalls);
console.log("디펜던시 조건", winBalls.length === 3);
useEffect(() => {
console.log("useEffect 실행!");
for (let i = 0; i < winNumbers.length - 1; i++) {
timeouts.current[i] = setTimeout(() => {
setWinBalls((prevBalls) => [...prevBalls, winNumbers[i]]);
}, (i + 1) * 1000);
}
timeouts.current[6] = setTimeout(() => {
setBonus(winNumbers[6]);
setRedo(true);
}, 7000);
return () => {
console.log("언마운트");
timeouts.current.forEach((v) => {
clearTimeout(v);
});
};
}, [winBalls.length === 3]);
의존성 배열에 winBalls.length === 3
가 들어가있는데,
곳곳에 console.log를 찍어두고 실행하면 아래의 결과가 나온다.
컴포넌트가 처음으로 Mount 되고 useEffect가 실행됐을 때, winBalls.length === 3
은 false이고 winBalls에 숫자를 1초에 하나씩 담기 시작한다.
그러다 3개를 담았을 때 winBalls.length === 3
가 true가 되고 이전 값(false)와 달라졌으니 컴포넌트가 unMount되고 다시 Mount되면서 useEffect가 두번째로 실행된다.
두번째 실행에서 winBalls 배열에 숫자가 하나 더 추가되어 length가 4가 되면, winBalls.length === 3
은 false가 되고 이전 값(true)와 달라져서 컴포넌트가 unMount, 다시 Mount, useEffect가 실행된다.
그 뒤론 숫자가 추가되어도 winBalls.length === 3
는 false이므로 useEffect는 다시 실행되지 않는다.
useEffect는 컴포넌트가 componentDidMount일 때 실행되는 걸 막을 수는 없다.
그러니 첫 실행 때는 아무 것도 하지 않고 의존성 배열에 넣어둔 값이 바뀔 때만 동작을 실행하도록 하는 꼼수를 사용한다.
const mounted = useRef(false)
useEffect(() => {
if (!mounted.current) {
mounted.current = true;
} else {
// 실행할 작업
}
}, [바뀌는 값]);