[React] 웹 게임-5 가위바위보

ji_silver·2020년 7월 30일
0

[React] 웹 게임

목록 보기
5/8
post-thumbnail

1. 리액트 라이프사이클

  • componentDidMount(): 처음 render가 성공적으로 됐다면 실행, 리랜더링이 되면 실행x (비동기 요청)
  • componentDidUpdate(): 리랜더링 후 실행
  • componentWillUnmount(): 컴포넌트가 제거되기 직전 실행 (비동기 요청 정리)
  • 클래스의 경우
    constructor -> render -> ref -> componentDidMount ->
    (setState / props 바뀔 때) -> shouldComponentUpdate(true) -> render -> componentDidUpdate ->
    부모가 나를 없앴을 때 -> componentWillUnmount -> 소멸

2. setInterval과 라이프사이클 연동하기

interval;

componentDidMount() {
    this.interval = setInterval(() => {
      ...
    }, 1000);
}
                                
componentWillUnmount() {
    clearInterval(this.interval);
}

setInterval(), setTimeout()은 없애주지 않으면 메모리 누수가 생기므로 완료되지 않은 비동기 요청은 componentWillUnmount()에서 정리해주기

❗ 비동기 안에서 바깥에 있는 함수를 참조하면 클로저 문제 발생 항상 주의

3. 가위바위보 게임 만들기

RSP.jsx

import React, { Component } from 'react';

const rspCoords = {
    바위: '0',
    가위: '-260px',: '-538px'
};

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: '',
        imgCoord: '0',
        score: 0,
    };

    interval;

    componentDidMount() {
        this.interval = setInterval(this.changeHand, 100);
    }

    componentWillUnmount() {
        clearInterval(this.interval);
    }

    changeHand = () => {
        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.바위,
            });
        }
    }
    
    // 버튼 클릭 시 인터벌 잠시 멈추고 점수 계산 후 다시 실행
    onClickBtn = (choice) => {
        const { imgCoord } = this.state;
        clearInterval(this.interval);
        const myScore = scores[choice];
        const cpuScore = scores[computerChoice(imgCoord)];
        const diff = myScore - cpuScore; // 내 스코어와 컴퓨터 스코어 빼기
        if (diff === 0) { // 차이가 없으면
            this.setState({
                result: '비겼습니다!',
            });
        } else if ([-1, 2].includes(diff)) {
            this.setState((prevState) => {
                return {
                    result: '이겼습니다!',
                    score: prevState.score + 1,
                };
            });
        } else {
            this.setState((prevState) => {
                return {
                    result: '졌습니다!',
                    score: prevState.score - 1,
                };
            });
        }
        setTimeout(() => {
            this.interval = setInterval(this.changeHand, 100);
        }, 2000); // 결과 나온 후 2초 기다렸다가 다시 돌리기
    };

    render() {
        const { result, score, imgCoord } = this.state;
        return (
            <>
                <div id="computer" style={{ background: `url(https://data.ac-illust.com/data/thumbnails/4f/4f63b32d7d43ea2cb231c0724200cf8e_t.jpeg) ${imgCoord} 0` }}></div>
                <div>
                    <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>
            </>
        );
    }
}

export default RSP;

4. 고차 함수

메서드 안에 함수를 호출하는 부분이 있으면 화살표 함수를 빼서 함수를 연달아 쓸 수 있음

onClickBtn = (choice) => () => {
  ...
};
  
...

render() {
  return (
    <button onClick={this.onClickBtn('바위')}>바위</button>
    <button onClick={this.onClickBtn('가위')}>가위</button>
    <button onClick={this.onClickBtn('보')}></button>
  );
}

5. Hooks와 useEffect

Hooks엔 LifeCycle이 없지만 useEffect로 동일하게 동작하게 할 수 있음

useEffect(() => { // componentDidMount, componentDidUpadate 역할 (1대1 대응은 아님)
    interval.current = setInterval(changeHand, 100);
    return () => { //componentWillUnmount 역할
        clearInterval(interval.current);
    }
}, [imgCoord]);
//두번째 인수 배열에 넣은 값(imgCoord)이 바뀔 때 useEffect 실행 (useEffect를 다시 실행할 값만 넣어주기)

매번 clearInterval()을 하기 때문에 setTimeout() 하는 것과 동일

👉 state가 업데이트 될 때 마다 useEffect가 실행 후 종료가 되므로 setTimeout()을 사용해도 됨!

6. 클래스와 Hooks 라이프사이클 비교

  • 클래스: componentDidMount, componentDidUpadate, componentWillUnmount로 state를 한 번에 조작할 수 있음
  • Hooks: useEffect로 state를 개별로 담당. useEffect는 여러 개 작성 가능

profile
🚧개발중🚧

0개의 댓글