리액트 class/hooks로 구현한
로또 게임 입니다.
로또 게임 입니다.
예제는 인프런의 제로초, "조현영"님의 강의를 들으면서 공부한 내용입니다.
순수 자바스크립트로 만든 로또 게임은 아래 포스트로 이동해주세요.
https://goddino.tistory.com/103
client.jsx
import React from 'react';
import ReactDom from 'react-dom';
import Lotto from './Lotto';
ReactDom.render(<Lotto />, document.querySelector('#root'));
index.html
<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>로또 추첨기</title>
<style>
.ball { display: inline-block; width: 30px; height: 30px; margin: 10px;
line-height: 30px; text-align: center; border-radius: 50%; }
</style>
</head>
<body>
<div id="root"></div>
<script src="./dist/app.js"></script>
</body>
</html>
Lotto.jsx - class 방식
import React, { Component } from 'react';
import Ball from './Ball'
function getLottoNums() {
console.log('getLottoNums');
//숫자 45개 뽑기
const candidates = Array(45).fill().map((v, i) => v = i+1 );
const chosenNum = [];
while (candidates.length > 0) { //45개 후보숫자 랜덤 섞기
const mixedNum = candidates.splice(Math.floor(Math.random() * candidates.length), 1)[0];
chosenNum.push(mixedNum);
}
console.log(chosenNum);
const lottoNum = chosenNum.slice(0, 6).sort((p, c) => p - c ); //배열
const bonus = chosenNum[chosenNum.length -1];
console.log(lottoNum, bonus);
return [...lottoNum, bonus]
}
class Lotto extends Component {
state = {
lottoNum: getLottoNums(), //당첨숫자들
winBalls: [], //lottoNum의 숫자를 winBalls에 넣어줌
bonus: null, //보너스공, lottoNum의 마지막 숫자를 보너스로 넣어줌
redo: false, //한번더 버튼 true시 출현
}
timeouts = [];
runTimeouts = () => {
const {lottoNum} = this.state;
for(let i=0; i<lottoNum.length -1; i++) { //0-5숫자
this.timeouts[i] = setTimeout(() => {
//lottoNum를 winBalls에 push 대체법
this.setState((prevState) => {
return {
winBalls: [...prevState.winBalls, lottoNum[i]],
};
});
}, i * 1000);
}
this.timeouts[6] = setTimeout(() => { //마지막 숫자
this.setState({
bonus: lottoNum[6],
redo: true, //true시 한번더 버튼 출현
});
}, 7000);
};
componentDidMount() { //시작하자마자 실행
this.runTimeouts();
}
//before state가 prevState, after state가 this.state
componentDidUpdate(prevProps, prevState) { //업데이트 상황을 조건문으로 지정 중요
if(this.state.winBalls.length === 0) { //winBalls: [] <- 아래 초기화 문구
this.runTimeouts();
}
}
componentWillUnmount() {
this.timeouts.forEach((item) => {
clearTimeout(item);
});
}
onClickRedo = () => {
this.setState({ //초기화
lottoNum: getLottoNums(),
winBalls: [],
bonus: null,
redo: false,
});
this.timeouts = [];
}
render() {
const {winBalls, bonus, redo} = this.state;
return (
<div>
<div>당첨 숫자</div>
<div id="결과창">
{/* 반복되는 부분은 컴포넌트화 */}
{winBalls.map((num) => <Ball key={num} number={num} /> )}
</div>
<div>보너스</div>
{/* 조건부 실행문 */}
{bonus && <Ball number={bonus} />}
{redo && <button onClick={redo? this.onClickRedo : () => {}}>한번 더!</button>}
</div>
);
}
}
export default Lotto;
Lotto.jsx - hooks 방식
import React, { useState, useRef, useEffect, useMemo } from 'react';
import Ball from './Ball'
function getLottoNums() {
console.log('getLottoNums');
//숫자 45개 뽑기
const candidates = Array(45).fill().map((v, i) => v = i+1 );
const chosenNum = [];
while (candidates.length > 0) { //45개 후보숫자 랜덤 섞기
const mixedNum = candidates.splice(Math.floor(Math.random() * candidates.length), 1)[0];
chosenNum.push(mixedNum);
}
console.log(chosenNum);
const lottoNum = chosenNum.slice(0, 6).sort((p, c) => p - c ); //배열
const bonus = chosenNum[chosenNum.length -1];
console.log(lottoNum, bonus);
return [...lottoNum, bonus]
}
//비동기 요청 처리
//componentDidMount만 실행할때
//useEffect(() => {
//ajax
//}, []);
//const mounted = useRef(false);
//useEffect(() => {
// if(!mounted.current) { //실행은 되지만 아무것도 안함
// mounted.current = true;
// }else{
// //ajax
// }
//},[바뀌는 값]); //componentDidUpdate만 componentDidMount 안함
const Lotto = () => {
//getLottoNums()가 계속 재실행 되는 것을 막기 위하여 useMemo() 사용
const getLottoNumsMemo = useMemo(() => getLottoNums(), []); //빈배열이면 getLottoNums()이 재실행되지 않음
const [lottoNum, setLottoNum] = useState(getLottoNumsMemo); //getLottoNums()에서getLottoNumsMemo로 바꿈
const [winBalls, setWinBalls] = useState([]);
const [bonus, setBonus] = useState(null);
const [redo, setRedo] = useState(false);
const timeouts = useRef([]);
useEffect(() => {
for(let i=0; i<lottoNum.length -1; i++) { //0-5숫자
timeouts.current[i] = setTimeout(() => {
//lottoNum를 winBalls에 push 대체법
setWinBalls((prev) =>[...prev, lottoNum[i]]);
}, i * 1000);
}
timeouts.current[6] = setTimeout(() => { //마지막 숫자
setBonus(lottoNum[6],),
setRedo(true);
}, 7000);
return () => {
timeouts.current.forEach((v) => clearTimeout(v));
}
}, [timeouts.current]); //아래 55번줄 바뀌는 시점 넣어줌
//[]이 빈배열이면 componentDidMount와 동일
//배열에 요소가 있으면 componentDidMount와 componentDidUpdate 둘다 수행
const onClickRedo = () => {
console.log('onClickRedo');
setLottoNum(getLottoNums());
setWinBalls([]);
setBonus(null);
setRedo(false);
timeouts.current = [];
}
return (
<div>
<div>당첨 숫자</div>
<div id="결과창">
{/* 반복되는 부분은 컴포넌트화 */}
{winBalls.map((num) => <Ball key={num} number={num} /> )}
</div>
<div>보너스</div>
{/* 조건부 실행문 */}
{bonus && <Ball number={bonus} />}
{redo && <button onClick={redo? onClickRedo : () => {}}>한번 더!</button>}
</div>
);
}
export default Lotto;
Ball.jsx
import React, { PureComponent } from 'react';
class Ball extends PureComponent {
render() {
let background;
const {number} = this.props;
if (number < 10) {
background = 'yellow';
} else if (number < 20) {
background = 'aqua';
} else if (number < 30) {
background = 'pink';
} else if (number < 40) {
background = 'skyblue';
} else { background = 'gold'; }
return (
<>
<div className='ball' style={{background}}>{number}</div>
</>
)
}
}
export default Ball;
반응형
'개발 > React' 카테고리의 다른 글
[react] react의 useReducer 사용법 (0) | 2021.04.06 |
---|---|
[react] react로 틱택토 게임 만들기(ft. useReducer) (0) | 2021.04.06 |
[react] react로 가위바위보 게임 만들기 (0) | 2021.04.01 |
[react] react로 반응속도 테스트 게임 만들기(ft. useRef, 조건문, spread 연산자) (0) | 2021.03.24 |
[react] 리액트 끝말잇기 게임 만들기(ft. 컴포넌트 구조) (0) | 2021.03.24 |
댓글