7-7. Hooks - useRef

송한솔·2023년 4월 27일
0

ReactStudy

목록 보기
37/54

useRef

useRef Hook은 함수 컴포넌트에서 ref를 쉽게 사용할 수 있도록 해 줍니다.

Average.js에서 등록 버튼을 눌렀을 때,

포커스가 인풋 쪽으로 넘어가도록 코드를 작성해 보겟습니다.

Average.js

import { useState, useMemo, useCallback, useRef } from 'react';

const getAverage = numbers => {
    console.log("평균값 계산 중..");
    if(numbers.length === 0) return 0;
    //reduce = 누적 연산(이 경우엔 배열의 모든 요소를 하나의 값(sum)에 누적합니다)
    const sum = numbers.reduce((a,b) => a+b);
    return sum / numbers.length;
};

const Average = () => {

const [list, setList] = useState([]);
const [number, setNumber] = useState('');
const inputEl = useRef(null);

const onChange = useCallback(e => {
    setNumber(e.target.value);
}, []); // 컴포넌트가 처음 렌더링될 때만 함수 생성

const onInsert = useCallback(e => {
    const nextList = list.concat(parseInt(number));
    setList(nextList);
    setNumber('');
    inputEl.current.focus();
}, [number, list]); // number 혹은 list가 변경되었을 때만 함수 생성

const avg = useMemo(()=>getAverage(list), [list]);

    return (
        <div>
            <input value={number} onChange={onChange} ref={inputEl}/>
            <button onClick={onInsert}>등록</button>
            <ul>
                {list.map((value, index) => (<li key={index}>{value}</li>))}
            </ul>
            <p><b>평균값:</b>{avg}</p>
        </div>
    );
};

export default Average;

등록 버튼을 누른 후에도 input에 focus가 유지되는 것을 볼 수 있습니다.


로컬 변수 사용하기

추가로 컴포넌트 로컬 변수를 사용해야 할 때도 useRef를 사용할 수 있습니다.

여기서 로컬 변수란 렌더링과 상관없이 바뀔 수 있는 값을 의미합니다.

클래스 컴포넌트의 경우 로컬 변수를 사용해야 할 때 다음과 같이 작성할 수 있습니다.

import { useState, useRef } from "react";

const MyComponent2 = () => {
  const [count, setCount] = useState(0);
  const timerId = useRef(null); // 로컬 변수

  const startTimer = () => {
    if (timerId.current) return; // 이미 타이머가 실행 중인 경우 무시합니다.
		// 1초마다 prevCount에 +1
    timerId.current = setInterval(() => {
      setCount((prevCount) => prevCount + 1);
    }, 1000);
  };

  const stopTimer = () => {
    clearInterval(timerId.current);
    timerId.current = null;
  };

  return (
    <div>
      <p>Count: {count}</p>
      <button onClick={startTimer}>Start Timer</button>
      <button onClick={stopTimer}>Stop Timer</button>
    </div>
  );
};

export default MyComponent2;

여기에서 컴포넌트가 리렌더링 되지 않는다는 의미는

로컬 변수인 timerId의 상태,

즉 timerId.current의 값이 null / setInterval(() +>{…}); 두가지로 변경되어도 state변경에 의한 리렌더링이 이루어지 않는다는 의미입니다.


Average.js 예제로 로컬 변수 좀더 쉽게 이해하기

import { useState, useMemo, useCallback, useRef } from 'react';

const getAverage = numbers => {
    console.log("평균값 계산 중..");
    if(numbers.length === 0) return 0;
    //reduce = 누적 연산(이 경우엔 배열의 모든 요소를 하나의 값(sum)에 누적합니다)
    const sum = numbers.reduce((a,b) => a+b);
    return sum / numbers.length;
};

const Average = () => {

const [list, setList] = useState([]);
const [number, setNumber] = useState('');
const inputEl = useRef(null);

const onChange = useCallback(e => {
    setNumber(e.target.value);
}, []); // 컴포넌트가 처음 렌더링될 때만 함수 생성

const onInsert = useCallback(e => {
    const nextList = list.concat(parseInt(number));
    setList(nextList);
    setNumber('');
    inputEl.current.focus();
}, [number, list]); // number 혹은 list가 변경되었을 때만 함수 생성

const avg = useMemo(()=>getAverage(list), [list]);

    return (
        <div>
            <input value={number} onChange={onChange} ref={inputEl}/>
            <button onClick={onInsert}>등록</button>
            <ul>
                {list.map((value, index) => (<li key={index}>{value}</li>))}
            </ul>
            <p><b>평균값:</b>{avg}</p>
        </div>
    );
};

export default Average;

위의 코드에서 const inputEl = useRef(null);는 로컬 변수를 의미하며

이 경우에 리렌더링 되지 않는다는 것은 onInsert 함수에서 inputEl.current.focus(); 코드가 실행될 때 컴포넌트가 리렌더링되지 않는다는 의미입니다.

0개의 댓글