자바스크립트에서 특정 DOM을 선택해야할 때는 DOM Selector를 사용한다. 리액트에서도 특정 요소의 크기를 가져온다거나, 포커스를 설정해야 한다거나 특정 DOM을 선택해야 할 상황이 있다.
이런 경우, 리액트 함수형 컴포넌트에서는 react hook 중 하나인 useRef함수를 사용한다.
클래스형 컴포넌트에서는 콜백함수를 사용하거나 React.createRef함수를 사용한다.
const element = useRef();
<input onChange={onChange} value={text} ref={element} />
element.current.focus();
초기화 버튼을 누르면 input태그에 있는 값이 초기화되고 focus가 잡히는 기능을 구현하였다.
import React, {useState, useRef} from 'react';
const useRefPrac = (initialText) => {
const [text, setText] = useState(initialText);
const element = useRef();
const onChange = (e) => {
const {target:{value}} = e;
setText(value);
}
const onReset = (e) => {
e.preventDefault();
setText('');
element.current.focus();
}
return {text,element, onReset, onChange};
}
const App = () => {
const {text, element, onReset, onChange} = useRefPrac("text를 입력하세요");
return(
<div className="App">
<input onChange={onChange} ref={element} value={text}/>
<button onClick={onReset}>초기화</button>
</div>
)
}
export default App;
before
after
useRef hook은 DOM 선택 용도 외에도, 컴포넌트 안에서 조회 및 수정 가능한 변수를 관리하는 용도가 있다. useRef로 변수를 관리하게 되면, 변수가 업데이트 될 때 컴포넌트가 리렌더링 되지 않고, 컴포넌트가 렌더링 될 때 변수의 값이 초기화 되지도 않는다. 따라서 useRef 로 정의된 변수는 항상 stable 한 상태를 유지할 수 있으며, 결과적으로 변수를 참조하는 함수는 실행될때마다 항상 현재(최신)의 값을 참조할 수 있다.
useRef를 활용한 변수는 아래와 같은 곳에 쓰인다.
예시
import React, {useState, useEffect, useRef} from 'react';
import UserList from "./UserList";
function App(){
const users = [
{ id:1, username: 'henry', email: 'henry@email.com' },
{ id:2, username: 'malia', email: 'maliay@email.com' },
{ id:3, username: 'tomson', email: 'tomsony@email.com'}
];
const nextId = useRef(4);
const onCreate = () => {
//배열에 새로운 항목 추가하는 로직 생략
nextId.current += 1;
};
return <UserList users={users}/>;
}
export default App;
useRef()함수를 이용해 고유값 변수로 nextId를 설정해주고 인자로 id가 될 숫자 4를 넣어준다.
따라서 인자로 넣어준 값이 변수의 current값이 된다. nextId변수를 수정하거나 조회하려면 .current값을 수정하거나 조회한다.
추가 예시: useCallback 에서 사용되는 useRef 변수
setInterval이나 setTimeout과 같은 함수는 clear시켜주지 않으면 메모리를 많이 소모한다. 따라서 함수를 구현하고 컴포넌트가 unmount될때나 특정 상황에서 clear해줄 필요가 있다.
예시
function Timer() {
const intervalRef = useRef();
useEffect(() => {
const id = setInterval(() => {
// ...
});
intervalRef.current = id;
return () => {
clearInterval(intervalRef.current);
//컴포넌트가 unmount될때 clearInterval을 활용해서
//setInterval함수가 들어있는 ref객체를 초기화해준다.
};
});
// ...
interval이나 timeout을 설정할때는 ref가 필요하지 않지만, 설정된 것을 지울 때 유용하다.
ref prop 관련해서 한 가지 주의할 점은 HTML 엘리먼트에 직접 제어하는 것은 JQuery 시절에 주로 쓰이던 imperative(명령형) 방식의 웹 프로그래밍이라는 것이다. Declarative(선언형) 프로그래밍 패러다임을 기반으로 하는 React를 포함한 모던 자바스크립트 라이브러리에서는 반드시 필요한 경우가 아니라면 이러한 접근 방식은 지양하는 것이 좋다.
[출처 및 참고]
ko.reactjs
xiubindev.tistory
DaleSeo/react-refs