.current프로퍼티로 전달된 인자(initialValue)로 초기화된 변경 가능한 ref 객체를 반환
- useState와 다르게 값이 바뀌어도 return 부분의 jsx가 렌더링이 진행되지 않는다.
- 추가로 렌더링이 되어도 동일한 참조값을 유지하기 위하여 사용한다.
- 컴포넌트에 focus를 위치시킬 필요가 있는 경우
- setTimeout과 같은 것의 초기화
import React, { useState, useRef, useCallback, useMemo } from "react";
let timeout = null;
let startTime = 0;
let endTime = 0;
const ResponseCheck = () => {
const [state, setState] = useState("waiting");
const [message, setMessage] = useState("클릭해서 시작하세요.");
const [result, setResult] = useState([]);
const onClickScreen = useCallback(() => {
if (state === "waiting") {
timeout = setTimeout(() => {
setState("now");
setMessage("지금 클릭");
startTime = new Date();
}, Math.floor(Math.random() * 1000) + 2000); // 2초~3초 랜덤
setState("ready");
setMessage("초록색이 되면 클릭하세요.");
} else if (state === "ready") {
// 성급하게 클릭
clearTimeout(timeout);
setState("waiting");
setMessage("너무 성급하시군요! 초록색이 된 후에 클릭하세요.");
} else if (state === "now") {
// 반응속도 체크
endTime = new Date();
setState("waiting");
setMessage("클릭해서 시작하세요.");
setResult((prevResult) => {
return [...prevResult, endTime - startTime];
});
}
}, [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;
timeout startTime endTime을 컴포넌트에서 뺀 이유는 렌더링이 진행되면 해당 값들은 기존 값으로 변경되기 때문에 렌더링이 되어도 해당 값을 유지하기 위하여 컴포넌트 밖으로 뺐다.
(물론 좋은 방법은 아니라고 생각한다.)
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;
useRef를 이용하여 컴포넌트 내부에
timeout startTime endTime를 정의하였고 해당 부분은 여러 렌더링이 일어나도 이에 값이 영향받지않는다.