-> 일정 시간 텀을 두기 위해 사용
-> hook에서는 ref가 처리
-> 사용 후 언제나 clear 해줘야 함
-> 컴포넌트 시작되자마자 setTimeout 실행 됨. 1초, 2초, 3초....뒤에 공이 등장함
-> setTimeout 사용했으면, 꼭 clear 해줘야 함
-> 한 번 더 버튼 누르면, 초기화해주고 setTimeout 다시 실행해줘야 함
-> 한 번 더 버튼을 눌렀을 때만, componentDidUpdate 수행하여 setTimeout 다시 실행하도록 해줘야 함
import React, { Component } from 'react';
import Ball from './Ball';
function getWinNumbers() { //숫자 미리 뽑아놓음
console.log('getWinNumbers');
const candidate = Array(45).fill().map((v, i) => i + 1);
const shuffle = [];
while (candidate.length > 0) {
shuffle.push(candidate.splice(Math.floor(Math.random() * candidate.length), 1)[0]);
}
const bonusNumber = shuffle[shuffle.length - 1];
const winNumbers = shuffle.slice(0, 6).sort((p, c) => p - c);
return [...winNumbers, bonusNumber];
}
class Lotto extends Component {
state = {
winNumbers: getWinNumbers(), // 당첨 숫자들
winBalls: [],
bonus: null, // 보너스 공
redo: false,
};
timeouts = [];
runTimeouts = () => {
console.log('runTimeouts');
const { winNumbers } = this.state;
for (let i = 0; i < winNumbers.length - 1; i++) {
this.timeouts[i] = setTimeout(() => {
this.setState((prevState) => {
return {
winBalls: [...prevState.winBalls, winNumbers[i]],
};
});
}, (i + 1) * 1000);
}
this.timeouts[6] = setTimeout(() => {
this.setState({
bonus: winNumbers[6],
redo: true,
});
}, 7000);
};
componentDidMount() {
console.log('didMount');
this.runTimeouts();
console.log('로또 숫자를 생성합니다.');
}
componentDidUpdate(prevProps, prevState) {
console.log('didUpdate');
if (this.state.winBalls.length === 0) {
this.runTimeouts();
}
if (prevState.winNumbers !== this.state.winNumbers) {
console.log('로또 숫자를 생성합니다.');
}
}
componentWillUnmount() {
this.timeouts.forEach((v) => {
clearTimeout(v);
});
}
onClickRedo = () => {
console.log('onClickRedo');
this.setState({
winNumbers: getWinNumbers(), // 당첨 숫자들
winBalls: [],
bonus: null, // 보너스 공
redo: false,
});
this.timeouts = [];
};
render() {
const { winBalls, bonus, redo } = this.state;
return (
<>
<div>당첨 숫자</div>
<div id="결과창">
{winBalls.map((v) => <Ball key={v} number={v} />)}
</div>
<div>보너스!</div>
{bonus && <Ball number={bonus} />}
{redo && <button onClick={this.onClickRedo}>한 번 더!</button>}
</>
);
}
}
export default Lotto;
-> 뒤에가 빈 배열이면, componentDidMount와 동일!
-> componentDidMount, componentDidUpadate 둘 다 수행
-> return 부분이 componentWillUnmount 역할
-> 기본적으로 componentDidMount 실행, 두번째 값이 맞으면 componentDidUpdate 실행
-> 두 번째 인자가 바뀌지 않는 한, 앞에꺼 다시 실행되지 않음. 불필요하게 get~함수가 여러 번 호출되는 것을 방지
import React, { useState, useRef, useEffect, useMemo, useCallback } from 'react';
import Ball from './Ball';
function getWinNumbers() {
console.log('getWinNumbers');
const candidate = Array(45).fill().map((v, i) => i + 1);
const shuffle = [];
while (candidate.length > 0) {
shuffle.push(candidate.splice(Math.floor(Math.random() * candidate.length), 1)[0]);
}
const bonusNumber = shuffle[shuffle.length - 1];
const winNumbers = shuffle.slice(0, 6).sort((p, c) => p - c);
return [...winNumbers, bonusNumber];
}
const Lotto = () => {
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([]);
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 () => {
timeouts.current.forEach((v) => {
clearTimeout(v);
});
};
}, [timeouts.current]); // 빈 배열이면 componentDidMount와 동일
// 배열에 요소가 있으면 componentDidMount랑 componentDidUpdate 둘 다 수행
useEffect(() => {
console.log('로또 숫자를 생성합니다.');
}, [winNumbers]);
const onClickRedo = useCallback(() => {
console.log('onClickRedo');
console.log(winNumbers);
setWinNumbers(getWinNumbers());
setWinBalls([]);
setBonus(null);
setRedo(false);
timeouts.current = [];
}, [winNumbers]);
return (
<>
<div>당첨 숫자</div>
<div id="결과창">
{winBalls.map((v) => <Ball key={v} number={v} />)}
</div>
<div>보너스!</div>
{bonus && <Ball number={bonus} onClick={onClickRedo} />}
{redo && <button onClick={onClickRedo}>한 번 더!</button>}
</>
);
};
export default Lotto;
⭕ 나의 언어로 정리:
setTimeout이나 setInterval과 같은 함수들 사용 시, componentDidMount~~와 같은 라이프사이클 함수를 사용한다.
그래서 이번 강의에서는 class component에서 라이프사이클 함수들 사용하는 방법과 그 다음에는 hooks로 변환시키는 방법을 공부하였다. (useEffect 사용 방법) 그리고, useMemo(return 값을 기억), useCallback(함수 자체 기억)을 익혔다.