TIL 22-04-12

thisisyjin·2022년 4월 12일
0

TIL 📝

목록 보기
20/113

React.js

Today I Learned ... react.js

🙋‍♂️ React.js Lecture

🙋‍ My Dev Blog


React Lecture CH 5

1 - 라이프사이클
2 - setInterval과 라이프사이클 연동

3 - 가위바위보 게임
4 - 고차함수 (Higher-order Func)
5 - useEffect
6 - 클래스 vs Hooks 라이프사이클 비교

가위바위보 게임

RSP.jsx

import React, { Component } from 'react';

const rspCoords = {
  바위: '0',
  가위: '-142px',: '-284px',
};

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',
  };

  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);
  };

  render() {
    const { result, score, imgCoord } = this.state;
    return (
      <>
        <div
          id="computer"
          style={{
            background: `url(https://en.pimg.jp/023/182/267/1/23182267.jpg) ${imgCoord} 0`,
          }}
        />
        <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;

<변경 사항>

  1. setInterval 안에 콜백을 밖으로 빼서 보기좋게
    -> this.changeHand

  1. onClickBtn 함수 작성
  • 1) clearInterval
  • 2) myScore 과 cpuScore을 비교하여
    승, 패를 가리는 로직 구현.

❕ 승패 결정 로직

가위 : 1 , 바위 : 0, 보 : -1 이므로
1. 내가 이기는경우

  • 바위로 이기면? -1
  • 가위로 이기면? 2
  • 보로 이기면? -1
    ✅ 이기는 경우에는 둘의 차이(diff)가 -1이나 2이다.
  1. 비기는 경우에는 차가 0이다.
  2. 지는 경우에는 else 처리.
  • if(diff === 0) 비김
  • else if(diff가 -1이나 2면?) 이김
  • else 짐

참고 - includes 메서드 (Array.prototype)

[1,2,3,4,5].includes(2)   // true
  • true/false를 반환하므로 조건식으로 많이 사용됨.

  1. 점수 보여준 후, 다시 시작하기 (interval)
  • setInterval(this.changeHand, 1000);
  1. computerChoice 함수 작성
  • 일반 함수임. 클래스 바깥에 작성하기.

❕ computerChoice 로직

const computerChoice = (imgCoord) => {
  return Object.entries(rspCoords).find(function (v) {
    return v[1] === imgCoord;
  })[0];
};
  • 예를 들어, '바위' 버튼을 눌렀다면?

  • 현재 상태인 this.state.

const cpuScore = scores[computerChoice(imgCoord)];

이므로 현재 상태인 this.state.imgCoord를 인수로 받는다.

computerChoice 함수 내에서
Object.entries(rspCoords)는

const rspCoords = {
  가위: '0',
  바위: '-142px',: '-284px',
};

Object.entries(rspCoords);
// [['가위', '0'], ['바위', '-142px'], ['보', '-284px']]

위와 같이 Object.entries로 rspCoords 객체의 키-값 쌍을 배열로 나타낸 후에,
Array.prototype.find()로 해당 콜백을 만족하는 첫번째 요소를 반환한다.

[['가위', '0'], ['바위', '-142px'], ['보', '-284px']]
  .find(function (v) {
    return v[1] === imgCoord;
  })[0];
// 여기서 v는 배열의 각 요소를 의미함.
// v[1]은 두번째 요소가 imgCoord
// (=인수로 전달받은것. 즉, this.state (현재 상태))

// find()는 해당 요소를 반환하고, 
// 뒤에 [0]을 붙여 첫번째 요소인 '가위' or '바위' or '보'를 반환.

Object.entries

  • 참고 문서
  • 객체 자체의 enumerable(순회가능) 속성의 key-value쌍 배열을 반환.
  • cf> for...in은 프로토타입 체인의 속성도 열거하지만, entries는 자신의 속성만 열거함.

Array.prototype.find

  • 참고 문서
  • 인수로 판별 함수가 들어감.
  • 만족하는 첫번째 요소의 값을 반환.

  1. setTimeout 해주기
  • 점수를 보여주기 위해 잠시 clearInterval을 해줬는데, 2초 후 다시 setInterval을 해준다.
    (2초 기다리지 않으면 clear해준 의미가 X)
setTimeout(() => {
      this.interval = setInterval(this.changeHand, 100);
    }, 2000);

고차함수

= Higher-order Function

// JSX
<button onClick={() => this.onClickBtn('바위')}>바위</button>

// onClickBtn 함수
onClickBtn = (choice) => {
    // 함수 내용 
}

위와 같이 '바위'라는 인수를 함수에 전달해주기 위해서는 함수를 실행하라는 의미에서
() => 를 꼭 적어줘야 한다.

(안그러면 Error: Maximum update depth exceeded 가 발생)

내 Error Breaking 로그 🔨

-> 함수를 호출하는 호출부()가 있다면 에러 발생.

해결방법 - 고차함수 (커링)

onClickBtn = (choice) => () => { // () => () => ** 이중.
    // 함수 내용
}

// JSX
<button onClick={this.onClickBtn('바위')}>바위</button>

+) 참고

  • 지금은 event 객체가 필요 없지만,
    만약 e.target.value나 e.preventDefault 등의 동작을 해서 event 객체가 필요하다면?
onClickBtn = (choice) => (e) => {
  // 함수 내용
}

// JSX
<button onClick={this.onClickBtn('바위')}>바위</button>
profile
기억은 한계가 있지만, 기록은 한계가 없다.

0개의 댓글