TIL 22-04-08 (3)

thisisyjin·2022년 4월 8일
0

TIL 📝

목록 보기
17/113

React.js

Today I Learned ... react.js

🙋‍♂️ React.js Lecture

🙋‍ My Dev Blog


React Lecture CH 4

1 - 리액트 조건문
2 - setTimeout (반응속도게임)
3 - 성능체크
4 - 반응속도게임 (Hooks)
5 - return 내부에 for, if 사용(제어문)-

리액트 조건문

  • 반응속도 테스트 게임
  • 처음 화면 클릭시 wating -> 녹색화면 되면 클릭 안내시 ready -> 클릭시 now 순으로 state가 바뀜.

초기 state, JSX 작성

import React, { Component } from 'react';

class ResponseCheck extends Component {
  state = {
    state: 'wating',
    message: '클릭해서 시작하세요',
    results: [],
  };

  onClickScreen = () => {};

  render() {
    return (
      <>
        <div
          id="screen"
          className={this.state.state}
          onClick={this.onClickScreen}
        >
          {this.state.message}
        </div>

        <div>
          평균 시간:{' '}
          {this.state.results.reduce((prev, cur) => prev + cur) /
            this.state.results.length}
          ms
        </div>
      </>
    );
  }
}

export default ResponseCheck;

  • return() 안에서, 즉 JSX 안에서는 for이나 if를 사용할 수 없다.
  • for(반복문)대신 map을 이용하고, if(조건문)대신 삼항 연산자나 단축평가(&&)를 이용한다.
  • 위 코드대로 실행하면, 에러가 발생함. (results가 빈 배열이라 reduce를 할수 없음)
  • results가 빈 배열이면(=lengh가 0이면) -> null을 반환하고, 아니면 <div> 반환하게.
  • ✅ JSX에서 undefined, false, null은 태그가 없음을 의미함!

-> 삼항 연산자 이용

{this.state.results.length === 0 ? null : (
          <div>
            평균 시간:
            {this.state.results.reduce((prev, cur) => prev + cur) /
              this.state.results.length}
            ms
          </div>
)}

❗️ 참고
빈 배열 []는 Falsy value가 아니다. !![]는 false지만, 조건식으로 사용시 true이다.
-> 따라서, 위에서는 results.length === 0을 조건으로 해야한다!

참고 - 2

  • 삼항연산자 ( ? : ) 말고도 단축평가 (&&)도 사용 가능
  • 좌항이 false면 false 리턴 / true면 우항의 값 리턴.
{this.state.results.length === 0 && (
          <div>
            평균 시간:
            {this.state.results.reduce((prev, cur) => prev + cur) /
              this.state.results.length}
            ms
          </div> 
)}

조건문 작성

  • 삼항연산자( ? : ) 사용.
  • 함수로 따로 빼줘서 렌더링 할때마다 반복할 일이 없도록 함 + 가독성
import React, { Component } from 'react';

class ResponseCheck extends Component {
  state = {
    state: 'wating',
    message: '클릭해서 시작하세요',
    results: [],
  };

  onClickScreen = () => {};

  renderAverage = () => {
    const { results } = this.state;  // this.state.results는 너무 길어서 가독성 X - 구조분해로 줄여줌
    return results.length === 0 ? null : (
      <div>
        평균 시간:
        {results.reduce((prev, cur) => prev + cur) /
          results.length}
        ms
      </div>
    );
  };

  render() {
    const { state, message } = this.state;
    return (
      <>
        <div id="screen" className={state} onClick={this.onClickScreen}>
          {message}
        </div>
        {this.renderAverage}
      </>
    );
  }
}

export default ResponseCheck;

onClick 함수 작성

1) waiting -> ready -(setTimeout)-> now


  onClickScreen = () => {
    const { state, message, results } = this.state;
    if (state === 'wating') {      // waiting 상태일때 클릭시
      this.setState({        
        state: 'ready',            // ready로 변경후, message 변경
        message: '초록색이 되면 클릭하세요.',
      });
      
      setTimeout(() => {        // 2-3초 후에 now로 변경 후, message 변경
        this.setState({
          state: 'now',
          message: '지금 클릭',
        });
      }, Math.floor(Math.random() * 1000) + 2000); // 2sec-3sec 사이 랜덤으로
    } else if (state === 'ready') {
      // 너무 빠릅니다 라는 문구 뜨게
    } else if (state === 'now') {
        // 클릭시 시간 뜨게
    }
  };

2) ready, now일때 로직

import React, { Component } from 'react';

class ResponseCheck extends Component {
  state = {
    state: 'waiting',
    message: '클릭해서 시작하세요',
    results: [],
  };

  onClickScreen = () => {
    const { state, message, results } = this.state;
    if (state === 'waiting') {
      this.setState({
        state: 'ready',
        message: '초록색이 되면 클릭하세요.',
      });
      setTimeout(() => {
        this.setState({
          state: 'now',
          message: '지금 클릭',
        });
      }, Math.floor(Math.random() * 1000) + 2000); // 2sec-3sec 사이 랜덤으로
    } else if (state === 'ready') {
      this.setState({
        state: 'waiting',
        message: '너무 빠릅니다. 초록색이 된 후에 클릭하세요.',
      });
    } else if (state === 'now') {
      this.setState({
        state: 'waiting',
        results: [], // 추후 수정 (result 추가하는것)
        message: '클릭해서 시작하세요.',
      });
    }
  };

  renderAverage = () => {
    const { results } = this.state;
    return results.length === 0 ? null : (
      <div>
        평균 시간:
        {results.reduce((prev, cur) => prev + cur) / results.length}
        ms
      </div>
    );
  };

  render() {
    const { state, message } = this.state;
    return (
      <>
        <div id="screen" className={state} onClick={this.onClickScreen}>
          {message}
        </div>
        {this.renderAverage}
      </>
    );
  }
}

export default ResponseCheck;
  • ready 상태에서 클릭하면, this.setState에 의해 state가 'waiting'이 되지만,
    setTimeout은 그대로 진행된다.
    -> 마지막에 state가 now가 되는 것을 알 수 있음.
    -> ✅ clearTimeout이 필요하다!
import React, { Component } from 'react';

class ResponseCheck extends Component {
  state = {
    state: 'waiting',
    message: '클릭해서 시작하세요',
    results: [],
  };

  timeout;    // ❕ 1. this.timeout을 선언해줌 (그냥 함수 몸체에 선언하면 됨)

  onClickScreen = () => {
    const { state, message, results } = this.state;
    if (state === 'waiting') {
      this.setState({
        state: 'ready',
        message: '초록색이 되면 클릭하세요.',
      });
      this.timeout = setTimeout(() => {   // ❕ 2. setTimeout함수를 this.timeout에 할당
        this.setState({
          state: 'now',
          message: '지금 클릭',
        });
      }, Math.floor(Math.random() * 1000) + 2000); 
    } else if (state === 'ready') {
      clearTimeout(this.timeout);    // ❕ 3. clearTimeout() 해줌
      this.setState({
        state: 'waiting',
        message: '너무 빠릅니다. 초록색이 된 후에 클릭하세요.',
      });
    } else if (state === 'now') {
      this.setState({
        state: 'waiting',
        results: [...results], // 수정
        message: '클릭해서 시작하세요.',
      });
    }
  };

...

왜 미리 this.timeout을 선언해두었는지?

  • 만약 if문 안에서 timeout변수를 선언했다면 중복선언 불가이고,
    클래스의 프로퍼티(this.timeout)로 사용하려면, 클래스 내부(=몸체)에서 선언해야함.

3) 반응시간 측정

  • 반응시간 = state:now가 된 순간부터 -> 클릭을 한 순간까지.
    -> 즉, setTimeout을 할때 시작시간을 재고, else if(state === 'now')일때 클릭시간을 재서 두 시간의 차를 구함.
  • new Date() - 현재 시간 (Date 객체)

✅ 참고

  • 여기서 시작시간과 종료시간도 변하는 값이지만,
    렌더링이 일어날 필요는 없으므로 state가 아닌, 프로퍼티로 선언하기!
  • state가 바뀌면 다시 렌더링이 되지만, 프로퍼티는 바뀌어도 리렌더링 ❌
  • 렌더링이 일어나야 하는 것은? - state
  • 렌더링이 필요없는 변수는? - this.~~ (프로퍼티)로

import React, { Component } from 'react';

class ResponseCheck extends Component {
  state = {
    state: 'waiting',
    message: '클릭해서 시작하세요',
    results: [],
  };

  timeout;
  startTime;
  endTime;

  onClickScreen = () => {
    const { state, message, results } = this.state;
    if (state === 'waiting') {
      this.setState({
        state: 'ready',
        message: '초록색이 되면 클릭하세요.',
      });
      this.timeout = setTimeout(() => {
        this.setState({
          state: 'now',
          message: '지금 클릭',
        });
        this.startTime = new Date();
      }, Math.floor(Math.random() * 1000) + 2000); // 2sec-3sec 사이 랜덤으로
    } else if (state === 'ready') {
      clearTimeout(this.timeout);
      this.setState({
        state: 'waiting',
        message: '너무 빠릅니다. 초록색이 된 후에 클릭하세요.',
      });
    } else if (state === 'now') {
      this.endTime = new Date();
      this.setState((prevState) => {
        return {
          state: 'waiting',
          message: '클릭해서 시작하세요.',
          results: [...prevState.results, this.endTime - this.startTime], // push말고 spread를 써야함. (원본 변경 X)
        };
      });
      console.log(this.startTime, this.endTime, this.state.results);
    }
  };

  renderAverage = () => {
    const { results } = this.state;
    return results.length === 0 ? null : (
      <div>
        평균 시간:
        {results.reduce((prev, cur) => prev + cur) / results.length}
        ms
      </div>
    );
  };

  render() {
    const { state, message } = this.state;
    return (
      <>
        <div id="screen" className={state} onClick={this.onClickScreen}>
          {message}
        </div>
        {this.renderAverage()} 
      </>
    );
  }
}

export default ResponseCheck;

Error Breaking 🔨

  • 처음에 JSX 부분에 {this.renderAverage} 라고만 작성했었는데,
  • 함수이름 적기만 하니 함수 실행이 안된다.
  • 고로, 리턴값이 없다.
  • 반드시 호출부 ()와 함께 적어줘야 리턴값인 JSX가 나오게 된다.

실행 결과

  • 클릭 할때마다 -> results 배열에 값이 추가되고 -> 평균값이 변화함

순서도 + 정리

  • 클릭시마다 if-else로 현재 state를 검사함.
  • 반드시 setTimeout -> clearTimeout을 해줘야함.
  • 렌더링이 필요없는 timeout(setTimeout 할당용)이나 startTime, endTime은 프로퍼티로.
    (state로 설정시, 바뀔때마다 렌더링 발생)

profile
기억은 한계가 있지만, 기록은 한계가 없다.

0개의 댓글