④ 반응속도체크

hhkim·2020년 1월 26일
0
post-thumbnail

🔗 웹 게임을 만들며 배우는 React

1. React 조건문

💡
false, undefined, null은 jsx에서 태그없음을 의미한다.

  • react에서 조건문은 삼항연산자로 표현
  • reduce()의 인자로 빈 배열을 쓸 수 없기 때문에 조건문 처리를 해준다.
{/* result에 값이 없으면 null(태그없음), 있으면 div 태그 노출 */}
{this.state.result.length === 0 
    ? null 
    : <div>평균 시간: {this.state.result.reduce((a, c) => a + c) / this.state.result.length}ms</div>
}

2. setTimeout 넣어 반응속도체크

ResponseCheck.jsx

timeout;
startTime;
endTime;

onClickScreen = () => {
  const {state, message, result} = this.state;
  if(state === 'waiting') {
    this.setState({...});
    this.timeout = setTimeout(() => {	// setTimeout
      this.setState({...});
      this.startTime = new Date();
    }, Math.floor(Math.random() * 1000) + 2000);    // 2초~3초 랜덤
  } else if (state === 'ready'){  // 성급하게 클릭
    clearTimeout(this.timeout);	// timeout 삭제 (now 상태로 변하지 않도록)
    this.setState({...});
  } else if (state === 'now'){    // 반응속도 체크
    this.endTime = new Date();
    this.setState((prevState) => {
      return {
        state: 'waiting',
        result: [...prevState.result, this.endTime - this.startTime],
        message: '클릭해서 시작하세요.'
      }
    });
  }
};

3. 성능 체크와 Q&A

  • class에서는 변경이 있으면 render만 다시 실행되지만 hooks에서는 함수 전체가 다시 실행되므로 유의

4. 반응속도체크 Hooks로 전환하기

  • Hooks에서는 this의 속성들을 ref로 표현
  • 값이 바뀌기는 하지만 화면에 영향을 미치지 않는 경우 state가 아니라 ref 사용
  • ref는 current로 접근!

ReponseCheck.jsx

import React, { useState, useRef } from 'react';

const ResponseCheck = () => {
    const [state, setState] = useState('waiting');
    const [message, setMessage] = useState('클릭해서 시작하세요.');
    const [result, setResult] = useState([]);
    const timeout = useRef(null);
    const startTime = useRef();
    const endTime = useRef();

    const onClickScreen = () => {
        if(state === 'waiting') {
            setState('ready');
            setMessage('초록색이 되면 클릭하세요.');
            timeout.current = setTimeout(() => {
                setState('now');
                setMessage('지금 클릭');
                startTime.current = new Date();
            }, Math.floor(Math.random() * 1000) + 2000);    // 2초~3초 랜덤
        } else if (state === 'ready'){  // 성급하게 클릭
            clearTimeout(timeout.current);
            setState('waiting');
            setMessage('너무 성급하시군요! 초록색이 된 후에 클릭하세요.');
        } else if (state === 'now'){    // 반응속도 체크
            endTime.current = new Date();
            setState('waiting');
            setResult((prevResult) => {
                return [...prevResult, endTime.current - startTime.current]
            });
            setMessage('클릭해서 시작하세요.');
        }
    };

    const renderAverage = () => {
        return result.length === 0
            ? null
            : <>
             <div>평균 시간: {result.reduce((a, c) => a + c) / result.length}ms</div>
             <button onClick={onReset}>리셋</button>
            </>
    }

    const onReset = () => {
        setResult([]);
    };

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

export default ResponseCheck;

5. return 내부에 for과 if 쓰기

  • 즉시실행함수
    • 선언과 동시에 실행되는 함수
    • return문에 중괄호({})로 js 함수를 쓸 수 있으므로 즉시실행함수를 선언하여 동작
  // 괄호로 감싸고 끝에 ()를 붙여 즉시실행함수로 만든다.
   {(() => {
     if(result.length === 0){
       return null;
     } else {
       return <>
         <div>평균 시간: {result.reduce((a, c) => a + c) / result.length}ms</div>
         <button onClick={onReset}>리셋</button>
         </>
     }
   })()}

💡
jsx에서는 배열을 바로 리턴할 수도 있다.

// key값 필수!
return [
    <div key="사과">사과</div>,
    <div key=""></div>,
    <div key=""></div>
];

0개의 댓글