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변경에 의한 리렌더링이 이루어지 않는다는 의미입니다.
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(); 코드가 실행될 때 컴포넌트가 리렌더링되지 않는다는 의미입니다.