4장에서 다시한번 상기시키면 좋을 것은 다음과 같다.
import React, { useState, useRef, useCallback, useMemo } from 'react';
const ResponseCheck = () => {
const [state, setState] = useState('waiting');
const [message, setMessage] = useState('클릭해서 시작하세요.');
const [result, setResult] = useState([]);
const timeout = useRef(null);
const startTime = useRef(0);
const endTime = useRef(0);
const onClickScreen = useCallback(() => {
if (state === 'waiting') {
timeout.current = setTimeout(() => {
setState('now');
setMessage('지금 클릭');
startTime.current = new Date();
}, Math.floor(Math.random() * 1000) + 2000); // 2초~3초 랜덤
setState('ready');
setMessage('초록색이 되면 클릭하세요.');
} else if (state === 'ready') { // 성급하게 클릭
clearTimeout(timeout.current);
setState('waiting');
setMessage('너무 성급하시군요! 초록색이 된 후에 클릭하세요.');
} else if (state === 'now') { // 반응속도 체크
endTime.current = new Date();
setState('waiting');
setMessage('클릭해서 시작하세요.');
setResult((prevResult) => {
return [...prevResult, endTime.current - startTime.current];
});
}
}, [state]);
const onReset = useCallback(() => {
setResult([]);
}, []);
const renderAverage = () => {
return result.length === 0
? null
: <>
<div>평균 시간: {result.reduce((a, c) => a + c) / result.length}ms</div>
<button onClick={onReset}>리셋</button>
</>
};
return (
<>
<div
id="screen"
className={state}
onClick={onClickScreen}
>
{message}
</div>
{renderAverage()}
</>
);
};
export default ResponseCheck;
다음 부분이다.
const renderAverage 와 같이 JSX 문을 함수로 따로 만들어서 렌더해주는 방법이다.
const renderAverage = () => {
return result.length === 0
? null
: <>
<div>평균 시간: {result.reduce((a, c) => a + c) / result.length}ms</div>
<button onClick={onReset}>리셋</button>
</>
};
return (
<>
...
{renderAverage()}
</>
);
useRef 의 다른 적용방법
useRef 는 DOM 엘리먼트에 접근하기 위함인 것을 알고 있다.
하지만 추가적으로 알게 된 것은 다음과 같다.
아래 코드를 보면 timeout, startTime, endTime 과 같은 변수들은 useRef 를 사용하여 선언된 것을 알 수 있다.
왜 굳이 useRef 를 사용했을까?
state 는 변경되면 함수형 컴포넌트의 경우 컴포넌트 코드가 재실행된다. 이때 useRef 를 사용하면 그냥 변수처럼 사용할 수 있지만 값이 변해도 리렌더가 발생하지 않기때문에 다음과 같이 사용하는 것이다.
useRef 는 엘리먼트에 접근하기위해 사용하지만, 변수를 선언할때도 사용할 수 있다.
또한 ref 로 선언한 변수에 접근할때는 항상 current 를 주의 하도록 하자.
import React, { useState, useRef, useCallback, useMemo } from 'react';
const ResponseCheck = () => {
const [state, setState] = useState('waiting');
const [message, setMessage] = useState('클릭해서 시작하세요.');
const [result, setResult] = useState([]);
const timeout = useRef(null);
const startTime = useRef(0);
const endTime = useRef(0);
const onClickScreen = useCallback(() => {
if (state === 'waiting') {
timeout.current = setTimeout(() => {
setState('now');
setMessage('지금 클릭');
startTime.current = new Date();
}, Math.floor(Math.random() * 1000) + 2000); // 2초~3초 랜덤
setState('ready');
setMessage('초록색이 되면 클릭하세요.');
} else if (state === 'ready') { // 성급하게 클릭
clearTimeout(timeout.current);
setState('waiting');
setMessage('너무 성급하시군요! 초록색이 된 후에 클릭하세요.');
} else if (state === 'now') { // 반응속도 체크
endTime.current = new Date();
setState('waiting');
setMessage('클릭해서 시작하세요.');
setResult((prevResult) => {
return [...prevResult, endTime.current - startTime.current];
});
}
}, [state]);
const onReset = useCallback(() => {
setResult([]);
}, []);
const renderAverage = () => {
return result.length === 0
? null
: <>
<div>평균 시간: {result.reduce((a, c) => a + c) / result.length}ms</div>
<button onClick={onReset}>리셋</button>
</>
};
return (
<>
<div
id="screen"
className={state}
onClick={onClickScreen}
>
{message}
</div>
{renderAverage()}
</>
);
};
export default ResponseCheck;
'공부기록 > 웹 개발' 카테고리의 다른 글
[웹 게임을 만들며 배우는 React] 6장 (0) | 2022.08.29 |
---|---|
[웹 게임을 만들며 배우는 React] 5장 (0) | 2022.08.29 |
[웹 게임을 만들며 배우는 React] 3장 (0) | 2022.08.25 |
Element type is invalid: expected a string (for built-in components) or a class/function (for composite components) but got: object. (0) | 2022.08.25 |
[웹 게임을 만들며 배우는 React] 2장 (0) | 2022.08.25 |