참조 (reference) 를 생성하고 관리하기 위해 사용되는 hook 이다.
함수형 컴포넌트 내에서 가변적인 상태를 유지하고 업데이트하는 데 사용된다.
참조라는 의미가 굉장히 모호하고 직관적이지 않다.
차근차근 알아보며 사용법을 익혀보자.
다음과 같은 useRef
를 사용한 변수가 있다고 하자.
const ref = useRef(value);
console.log(ref);
콘솔 결과는 다음과 같다.
{ current: value }
즉, useRef
는 객체를 생성하고 인자는 current
키에 저장된다.
이건 언제든지 변경이 가능하다.
ref.current = 'hello'; // { current: 'hello' }
ref.current = 'nice'; // { current: 'nice' }
컴포넌트가 생성되고 부터 업데이트 되고 언마운트 되기까지, 컴포넌트가 아무리 렌더링 되더라도 값은 유지된다.
컴포넌트가 언마운트 됐을 때 useRef
는 초기화된다.
보통 state
가 변하면 렌더링이 일어나고 그로 인해 컴포넌트 내부 변수들이 모두 초기화가 된다.
🔔 함수형 컴포넌트는 말그대로 '함수' 기 때문에 다시 함수가 호출되어 내부 변수들 역시 모두 다시 불리기 때문이다.
하지만, 렌더링에 영향을 받지 않고 값을 유지해야 되는 경우가 있을 수 있다. 이럴 때 useRef
를 사용한다.
즉, state
가 변해서 렌더링이 일어나더라도 useRef
값은 컴포넌트가 언마운트 되지 않는 이상 그대로 유지된다.
반대로, useRef
가 변하더라도 컴포넌트는 렌더링을 하지 않는다.
import {useRef, useState} from 'react';
import {Button, SafeAreaView, Text} from 'react-native';
function App(): JSX.Element {
const [count, setCount] = useState(0);
const countRef = useRef(0);
const handleState = () => {
setCount(count + 1);
};
const handleRef = () => {
countRef.current = countRef.current + 1;
console.log('handleRef: ', countRef.current);
};
return (
<SafeAreaView>
<Text>useState: {count}</Text>
<Text>useRef: {countRef.current}</Text>
<Button title="useState +" onPress={handleState} />
<Button title="useRef +" onPress={handleRef} />
</SafeAreaView>
);
}
export default App;
위 코드를 보자.
구현한 모습은 아래와 같다.
각각 useState
, useRef
로 구현 된 count 기능이다.
먼저, useState + 를 한번 눌러 useState
를 업데이트한 결과이다.
예상대로 +1 이 증가한 모습이다.
이번엔 useRef + 버튼을 5번 연속 눌러보자. 결과는 다음과 같다.
화면에 아무런 변화가 없는 것처럼 보인다.
하지만, 콘솔 결과는 다르다.
이미 countRef.current
는 숫자 1부터 5까지 5번 동안 상태가 변했다!
즉, 상태는 업데이트 됐지만, 아직 렌더링이 안되어 화면 상에 나타나지 않은 것이다!
그렇다면, 다시 useState + 를 눌러 렌더링을 시키면 결과는 다음과 같다.
useRef
의 최신 상태가 업데이트 된 모습을 볼 수 있다.
이로써 useRef
는 렌더링에 영향을 받지도 주지도 않으며, 라이프 사이클 동안 언마운트 전까지 값을 유지한다는 것을 알 수 있다.
우리가 로그인 창에 진입했을 때, 굳이 마우스나 손가락으로 ID 입력 칸을 클릭하지 않아도 자동으로 포커스가 가게하여 바로 키보드로 입력할 수 있게끔 유도하고 싶을 것이다.
이럴 때 useRef
의 focus
메소드를 이용하여 해당 input(DOM)에 접근할 수 있다. 마치 JavaScript 의 Document.querySelector()
와 동일하다고 볼 수 있다.
import {useEffect, useRef} from 'react';
import {Alert, Button, SafeAreaView, TextInput, View} from 'react-native';
function App(): JSX.Element {
const inputRef = useRef();
const textValueRef = useRef('');
useEffect(() => {
inputRef.current.focus();
}, []);
const login = () => {
Alert.alert(`반갑습니다! ${textValueRef.current} 님!`);
};
const handleInputChange = text => {
textValueRef.current = text;
};
return (
<SafeAreaView>
<TextInput
ref={inputRef}
placeholder="username"
onChangeText={handleInputChange}
/>
<Button title="login" onPress={login} />
</SafeAreaView>
);
}
export default App;
위 코드를 실행 후 앱의 첫 화면은 다음과 같다.
username을 입력하라는 input 박스에 자동으로 키보드를 입력하는 커서(focus)가 잡힌 것을 확인할 수 있다.
이는 컴포넌트가 마운트 되면 useEffect
를 실행하고, inputRef
변수에서 useRef
의 focus
메소드를 통해 자동으로 input 박스에 포커싱이 되도록 구현한 것이다.
텍스트를 입력 후 login 버튼을 누른 화면은 다음과 같다.
Alert에 표시 된 value는 textValueRef
이다.
이는 첫 번째 저장 용도의 useRef
로 보면 된다. TextInput
박스의 onChangeText
메소드를 통하여 들어오는 Text 값을 실시간으로 textValueRef
변수의 상태를 업데이트하였다.
하지만, useRef
특성상 값이 변하여도 렌더링에 영향을 주지 않기에 input 박스에 값을 입력하여도 컴포넌트 렌더링엔 아무런 영향이 없는 것이다.