useRef에 대하여

KHW·2021년 11월 17일

유튜브강의

목록 보기
9/9

useRef

.current 프로퍼티로 전달된 인자(initialValue)로 초기화된 변경 가능한 ref 객체를 반환

useRef 특징

  • useState와 다르게 값이 바뀌어도 return 부분의 jsx가 렌더링이 진행되지 않는다.
  • 추가로 렌더링이 되어도 동일한 참조값을 유지하기 위하여 사용한다.

useRef 역할

  1. 컴포넌트에 focus를 위치시킬 필요가 있는 경우
  2. setTimeout과 같은 것의 초기화

useRef 사용예시와 아닌예시

useRef 아닌 예시

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을 컴포넌트에서 뺀 이유는 렌더링이 진행되면 해당 값들은 기존 값으로 변경되기 때문에 렌더링이 되어도 해당 값을 유지하기 위하여 컴포넌트 밖으로 뺐다.
(물론 좋은 방법은 아니라고 생각한다.)

useRef 사용예시

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를 정의하였고 해당 부분은 여러 렌더링이 일어나도 이에 값이 영향받지않는다.

profile
나의 하루를 가능한 기억하고 즐기고 후회하지말자

0개의 댓글