리액트 class/hooks로 구현한
가위바위보 게임 입니다.
가위바위보 게임 입니다.
예제는 인프런의 제로초, "조현영"님의 강의를 들으면서 공부한 내용입니다.
client.jsx
import React from 'react';
import ReactDom from 'react-dom';
import RSP from './RSP';
ReactDom.render(<RSP />, 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>
#computer { height: 250px; width: 140px; }
</style>
</head>
<body>
<div id="root"></div>
<script src="./dist/app.js"></script>
</body>
</html>
RSP.jsx - class 방식(before 리팩토링)
import React, { Component } from 'react';
//클래스 경우 -> constructor -> render -> ref -> componentDidMount
// -> (setState/props 바뀔때) -> showComponentUpdate(return true 일 경우) -> render -> componentDidUpdate
// 부모가 나를 없앴을때 -> componentWillUnmount -> 소멸
const rspCoords = {
바위: 0,
가위: -100,
보: -270,
};
const scores = {
가위: 1,
바위: 0,
보: -1,
};
const computerChoice = (imgCoord) => {
return Object.entries(rspCoords).find(function(v) {
return v[1] === imgCoord;
});
};
class RSP extends Component {
state = {
result: '',
score: 0,
imgCoord: 0, //이미지 좌표
}
componentDidMount() { //처음 렌더링 된 직 후, 비동기 요청
this.interval = setInterval( () => {
//위에가 {} 안에 있어야 함(setInterval가 비동기이므로 클로저 문제)
const { imgCoord } = this.state;
if(imgCoord === rspCoords.바위) {
this.setState({
imgCoord: rspCoords.가위,
});
}else if(imgCoord === rspCoords.가위) {
this.setState({
imgCoord: rspCoords.보,
});
}else if(imgCoord === rspCoords.보) {
this.setState({
imgCoord: rspCoords.바위,
});
}
}, 200);
}
//컴포넌트가 제거되기 직전, 비동기 요청 수동으로 clear 처리 해야 함
componentWillUnmount() {
clearInterval(this.interval);
}
onClickBtn = (choice) => { //함수, choice = 바위가위보
clearInterval(this.interval);
const myScore = scores[choice];
const computerScore = scores[computerChoice(imgCoord)];
const diff = myScore - computerScore;
if(diff === 0) {
this.setState({
result: '비겼습니다.'
})
}else if ([-1, 2].includes(diff)) {
this.setState((prev) => {
return {
result: '이겼습니다.',
score: prev.score + 1,
}
});
}else{
this.setState((prev) => {
return {
result: '졌습니다',
score: prev.score + 1,
}
})
}
};
render() {
const { result, score, imgCoord } = this.state;
return (
<div>
<div id="computer"
style={{
background: `url(https://blog.kakaocdn.net/dn/dEfPIp/btqRxZvIwfQ/
ZxcvaXl4f4TG4S08igTeak/img.jpg)
${imgCoord}px 0`
}}></div>
<div>
<button id="rock" className="btn" onClick={() => onClickBtn('바위')}>바위</button>
<button id="scissor" className="btn" onClick={() => onClickBtn('가위')}>가위</button>
<button id="paper" className="btn" onClick={() => onClickBtn('보')}>보</button>
</div>
<div>{result}</div>
<div>현재 점수: {score}점</div>
</div>
);
}
}
export default RSP;
RSP.jsx - class 방식(after 리팩토링)
import React, { Component } from 'react';
const rspCoords = {
바위: 0,
가위: -100,
보: -270,
};
const scores = {
가위: 1,
바위: 0,
보: -1,
};
const computerChoice = (imgCoord) => {
return Object.entries(rspCoords).find(function(v) {
return v[1] === imgCoord;
})[0];
};
class RSP extends Component {
state = {
result: '',
score: 0,
imgCoord: 0, //이미지 좌표
}
componentDidMount() { //처음 렌더링 된 직 후, 비동기 요청
this.interval = setInterval( this.timeCtr, 200);
}
timeCtr = () => {
const { imgCoord } = this.state;
if(imgCoord === rspCoords.바위) {
this.setState({
imgCoord: rspCoords.가위,
});
}else if(imgCoord === rspCoords.가위) {
this.setState({
imgCoord: rspCoords.보,
});
}else if(imgCoord === rspCoords.보) {
this.setState({
imgCoord: rspCoords.바위,
});
}
}
componentWillUnmount() {
clearInterval(this.interval);
}
onClickBtn = (choice) => () => { //리팩토링 함수, choice = 바위가위보
const {imgCoord} = this.state; //구조분해
clearInterval(this.interval);
const myScore = scores[choice];
const computerScore = scores[computerChoice(imgCoord)];
const diff = myScore - computerScore;
if(diff === 0) {
this.setState({
result: '비겼습니다.'
})
}else if ([-1, 2].includes(diff)) {
this.setState((prev) => {
return {
result: '이겼습니다.',
score: prev.score + 1,
}
});
}else{
this.setState((prev) => {
return {
result: '졌습니다',
score: prev.score - 1,
}
})
}
setTimeout(() => {this.interval = setInterval(this.timeCtr, 200)},1000);
};
render() {
const { result, score, imgCoord } = this.state;
return (
<div>
<div id="computer"
style={{
background: `url(https://blog.kakaocdn.net/dn/dEfPIp/btqRxZvIwfQ
/ZxcvaXl4f4TG4S08igTeak/img.jpg) ${imgCoord}px 0`
}}></div>
<div>
{/* onClick 메서드 안에 함수를 호출하는 부분 리팩토링 () => 57번으로 자르고 복사 */}
<button id="rock" className="btn" onClick={this.onClickBtn('바위')}>바위</button>
<button id="scissor" className="btn" onClick={this.onClickBtn('가위')}>가위</button>
<button id="paper" className="btn" onClick={this.onClickBtn('보')}>보</button>
</div>
<div>{result}</div>
<div>현재 점수: {score}점</div>
</div>
);
}
}
export default RSP;
RSP.jsx -hooks 방식
import React, { useState, useRef, useEffect } from 'react';
const rspCoords = {
바위: 0,
가위: -100,
보: -270,
};
const scores = {
가위: 1,
바위: 0,
보: -1,
};
const computerChoice = (imgCoord) => {
return Object.entries(rspCoords).find(function(v) {
return v[1] === imgCoord;
})[0];
};
const RSP = () => {
const [result, setResult] = useState('');
const [score, setScore] = useState(0);
const [imgCoord, setImgCoord] = useState(rspCoords.바위); //rspCoords.바위 = 0;
const interval = useRef();
useEffect( () => { //componetDidMount, componentDidUpdate 역할(1대1 대응은 아님)
interval.current = setInterval(timeCtr, 200);
return () => { //componentWillUnmount 역할
clearInterval(interval.current);
}
}, [imgCoord]); //[] 값 = 바뀌는 state, 클로저 문제를 해결
const timeCtr = () => {
if(imgCoord === rspCoords.바위) {
setImgCoord(rspCoords.가위);
}else if(imgCoord === rspCoords.가위) {
setImgCoord(rspCoords.보);
}else if(imgCoord === rspCoords.보) {
setImgCoord(rspCoords.바위);
}
};
const onClickBtn = (choice) => () => {
clearInterval(interval.current);
const myScore = scores[choice];
const computerScore = scores[computerChoice(imgCoord)];
const diff = myScore - computerScore;
if(diff === 0) {
setResult('비겼습니다.');
}else if ([-1, 2].includes(diff)) {
setResult('이겼습니다.');
setScore((prevScore) => prevScore + 1);
}else{
setResult('졌습니다.');
setScore((prevScore) => prevScore -1);
}
setTimeout(() => {interval.current = setInterval(timeCtr, 200)},1000);
}
return (
<div>
<div id="computer"
style={{
background: `url(https://blog.kakaocdn.net/dn/dEfPIp/btqRxZvIwfQ
/ZxcvaXl4f4TG4S08igTeak/img.jpg) ${imgCoord}px 0`
}}></div>
<div>
{/* onClick 메서드 안에 함수를 호출하는 부분 리팩토링 () => 57번으로 자르고 복사 */}
<button id="rock" className="btn" onClick={onClickBtn('바위')}>바위</button>
<button id="scissor" className="btn" onClick={onClickBtn('가위')}>가위</button>
<button id="paper" className="btn" onClick={onClickBtn('보')}>보</button>
</div>
<div>{result}</div>
<div>현재 점수: {score}점</div>
</div>
);
}
export default RSP;
반응형
'개발 > React' 카테고리의 다른 글
[react] react로 틱택토 게임 만들기(ft. useReducer) (0) | 2021.04.06 |
---|---|
[js] react로 로또 게임 만들기 (0) | 2021.04.01 |
[react] react로 반응속도 테스트 게임 만들기(ft. useRef, 조건문, spread 연산자) (0) | 2021.03.24 |
[react] 리액트 끝말잇기 게임 만들기(ft. 컴포넌트 구조) (0) | 2021.03.24 |
[react] 리액트로 끝말잇기 게임 만들기 (2) | 2021.03.24 |
댓글