useRef
리액트 훅 중 useRef에 대해 알아보자. 보통 리액트에서 변하는 값을 다룰 때는 상태를 이용하게 된다. 또한, 리액트에서는 특정 DOM객체에 접근하고 싶을 때 document.querySelector과 같은 방법은 권장하지 않는다. 이럴 때 사용을 고려해볼 수 있는...
리액트 훅 중 useRef에 대해 알아보자.
보통 리액트에서 변하는 값을 다룰 때는 상태를 이용하게 된다. 또한, 리액트에서는 특정 DOM객체에 접근하고 싶을 때 document.querySelector과 같은 방법은 권장하지 않는다. 이럴 때 사용을 고려해볼 수 있는 것이 바로 ref이다.
useRef
함수 컴포넌트에서 사용하는 훅 중 useRef는 위에서 말한 경우에서 사용할 수 있다.
useRef는 .current프로퍼티로 전달된 인자로 초기화된 변경 가능한 ref객체를 반환한다.
아래의 예시를 보자
function App(){
const testRef = useRef(1);
console.log("render");
useEffect(() => {
console.log(testRef);
}, [testRef]);
return (<div>app</div>)
}
위 코드에서 useEffect를 이용해서 App컴포넌트가 마운트 될 때 inputRef를 출력해보았다.
그랬을 때 위와 같은 모습이 출력된다. 즉, useRef는 특별한 것이 아니라 current라는 프로퍼티의 값으로 초기에 넘겨준 인자를 할당하여 단순히 객체를 반환하는 것이다.
useRef를 쓰면 어떻게 될까?
useRef를 사용하게 되면 값을 저장할 수 있는 상자를 만든다고 생각하면 된다. 그러면 useState를 이용해서 상태를 만들면 되는 것이 아닌가?
하고 생각할 수 있지만, 둘은 차이가 있다. 값이 변할 때 상태의 경우 상태를 변화시키는 함수를 사용하게 되지만, useRef를 이용하면 직접 값을 바꿀 수 있다. 또한, 상태는 변경된다면 리렌더링이 일어나게 되는데 useRef를 이용하면 리렌더링이 일어나지 않는다는 차이가 있다.
또한 dom요소에 ref속성을 이용해서 해당 dom객체를 저장하고 있을 수 있다.
dom을 이용하는 법
그렇다면 DOM객체는 어떻게 가져올 수 있을까? 바로 ref속성을 이용하는 것이다. 아래 예시를 보자.
function App(){
const testRef = useRef(1);
console.log("render");
useEffect(() => {
console.log(testRef);
}, [testRef]);
return (<div ref={testRef}>app</div>)
}

위와 같은 결과가 나온다. ref에 useRef로 생성한 상자를 넣어주게 되면 그 dom객체가 저장되게 된다. 실제로 document.querySelector를 이용해서 특정 요소를 찾거나 하는 것과 비슷하다. 리액트에서 dom객체에 접근해야할 때 사용할 수 있다.
focus
주로 사용될 수 있는 경우는 input의 focus를 하는 것이다.
function App(){
const testRef = useRef();
useEffect(() => {
testRef.curretn.focus();
}, []);
return (<input ref={testRef}>app</input>)
}
위와 같이 작성하게 되면 처음 App컴포넌트가 마운트 될 때 input에 포커싱이 될 것이다.
리렌더링이 되지 않는다.
리렌더링이 되지 않는 점을 이용하면 다음과 같은 방식을 고려할 수 있다.
자동완성이 필요없는 input의 값을 가져올 때
보통 input값을 가져오게 된다면 input의 값을 저장할 상태를 만들고, onChange이벤트를 이용해서 값이 변할 때마다 상태도 변하게 하는 방법을 사용한다. 이 경우는 자동 완성 기능을 구현할 때 사용할 수 있다. 하지만, 단점은 input의 값이 변할 때마다 리렌더링이 계속된다는 것이다.
자동완성이 필요없다면 굳이 리렌더링이 될 필요가 없고, 엔터나 특정 버튼을 눌렀을 때만 값을 가지고 있으면 된다. 그때 useRef를 사용하면 리렌더링 없이 그 값을 가져올 수 있다. (변화할 때마다 값을 갱신할 수 있지만, 그냥 input.current.value를 가져오는 것이 더 편할 것이라 생각한다.)
const userModal =
(api) =>
({ setShow, setUser, title }) => {
const idRef = useRef();
const pwRef = useRef();
// const [id, setId] = useState("");
// const [pw, setPw] = useState("");
// const onIdChange = (e) => {
// setId(e.target.value);
// };
// const onPwChange = (e) => {
// setPw(e.target.value);
// };
const onSubmitClick = () => {
if (api(idRef.current.value, pwRef.current.value)) {
setUser(idRef.current.value);
setShow("");
}
};
return (
<Modal title={title} setShow={setShow}>
<ModalContent text="id">
<input className="modal__body" ref={idRef} />
</ModalContent>
<ModalContent text="password">
<input className="modal__body" ref={pwRef} />
</ModalContent>
<button className="modal__btnList--btn btn" onClick={onSubmitClick}>
SUBMIT
</button>
</Modal>
);
};
위의 코드는 실제로 내가 프로젝트에서 사용한 것이다. 모달창에 인풋을 띄워 아이디와 비밀번호를 입력받는데, 처음에는 그 값을 상태로 관리했지만, 필요없는 리렌더링이 생긴다고 판단하여 그냥 useRef를 이용해서 submit버튼을 눌렀을 때만 그 값을 가져오는 방식으로 수정했다. 실제로 렌더링은 줄어들었다. 좋은 효과라고 생각은 되지만, 인풋에 값을 입력하는 것이 큰 비용이 드는 리렌더링이 아닌 경우는(나의 프로젝트와 같은) 꼭 필요한 방식은 아니라고 생각한다.