오늘은 React의 든든한 국밥같은 친구 Ref에 대해서 정리해보자.
JS를 이용하여 앱을 만들 때 getElementById
와 같은 문법이 존재하였기 때문에 DOM의 요소를쉽게 스타일링하거나 지정, 지목할 수 있었다. 그런데 React에서도 이와 같은 작업을 하기 위해서는 어떻게 해야 할까?
똑같이 id 쓰면 되는 거 아닌가..?
결과부터 말하자면 아니다. 컴포넌트를 여러번 사용하게 되면 중복해서 id를 가지게 되는 경우가 나타난다. 그래서 잘못된다.
이러한 문제점을 단번에 해결해줄 수 있는 친구가 바로 ref이다. 이친구의 등장을 통하여 역경을 이겨내보자!
보통 state를 사용하여 해결할 수 있는 경우가 많지만 아래 기능은 state로 해결할 수 없다.
하지만 ref가 이렇게 든든한 국밥 친구 같다고 해서 남용하면 안됩니다.
컴포넌트에 ref를 달고, 그 ref를 다른 컴포넌트로 전달하고, 다른 컴포넌트에서 ref로 전달받은 컴포넌트의 메서드를 실행하는거 상식적으로 OK이지만, 리액트 사상에 어긋난 설계입니닷
앱 규모가 커지면 스파게티 코드되어서 유지보수가 아예 안되는 것입니다..
컴포넌트끼리 데이터를 교류할 때에는 언제나 부모와 자식이 자유롭게 데이터를 주고받아야 합니다.
예제를 통하여 이해해보자.
등록
버튼을 눌렀을 때, 포커스가 인풋 쪽으로 넘어가도록 코드를 작성해보자.
import { useState, useMemo, useCallback, useRef } from 'react';
import React from 'react';
const getAverage = numbers => {
console.log("평균값 계산 중 ...");
if (numbers.length === 0 ) return 0;
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]);
...
return (
<div>
<input value = {number} onChange = {onChange} ref = {inputEl}/>
<button onClick={onInsert}>등록</button>
</div>
)
이렇게 코드를 확인해보면, useRef
를 사용하여 ref
를 설정하면 객체 안의 current
값이 실제 엘리먼트를 가리키게 되는 것이다.
useRef
는 랜더링과 상관없이 그 값을 유지하고 있기 때문에 state를 신경쓰지 않고 어떤 변수를 관리할 때 우수한 성능을 보인다.
import { useRef } from 'react;
const RefSample = () => {
const id = useRef(1);
const setId = (n) => {
id.current = n;
}
const printId = () => {
console.log(id.current);
}
return (
<div>
refsample
</div>
);
};
export default RefSample;
이렇게 ref 안의 값이 바뀌어도, 컴포넌트가 랜더링되지 않는다는 점이 전역 변수로 활용할 수 있다. 랜더링과 관련되지 않는 것은 이렇게 useRef
를 이용하여 관리할 수 있다.