import { useRef } from 'react';
function MyComponent() {
const intervalRef = useRef(0);
const inputRef = useRef(null);
}
useRef
의 인수로는 초깃값을 넣어준다. useRef
는 current
라는 단일 프로퍼티를 갖는 객체를 반환하는데, 이 current
에 초깃값이 들어간다
useRef
는 값을 갖는 current
속성을 반환하고, current
는 값을 읽고, 쓸 수 있다. 값을 저장하고 다시 변경할 수 있다는 점에서 state
와 비슷해 보이지만. 중요한 차이점이 있다.
state
가 변경 되면 해당 state
가 포함된 컴포넌트를 리렌더링하지만, ref
는 변경되어도 컴포넌트가 리렌더링 되지 않는다.
따라서 useRef
는 정보를 저장하고, 나중에 쓰게 될 시각적으로는 표현되지 않는 값을 설정하는데 쓰면 유리하다. 예를 들어, setInterval
의 id등을 저장하는 경우이다.
function handleStartClick() {
const intervalId = setInterval(() => {
// ...
}, 1000);
intervalRef.current = intervalId;
}
값을 저장하고, 나중에 쓰는건 일반 변수가 하는 역할과 똑같다.
// 똑같은거 아닌가??
let ref = useRef(0);
let a = 0;
a = 1;
ref.current = 1;
하지만 역시 중요한 차이점이 있다.
컴포넌트가 리렌더링 될 경우, 변수는 값이 초기화 되지만, useRef
는 값을 계속 유지하고 있다.
다음 코드를 보자.
const ref = useRef(1);
let a = 1;
useEffect(() => {
// 컴포넌트가 리렌더링 될 때마다 실행!
console.log("ref:", ref);
console.log("a:", a);
});
const handleChange = () => {
a = 2;
ref.current = 2;
// state를 변경시켜 컴포넌트를 리렌더링 시킨다.
setState(Math.random());
};
예상 되는 결과로는 handleChange
함수에서 값을 변경했어도, 컴포넌트가 리렌더링 되면서 함수를 다시 호출하기 때문에 각각 다시 1로 초기화 될 것처럼 보인다.
하지만 결과처럼 리렌더링 되면 일반 변수 a
는 다시 초기화 되지만, ref
는 이전의 값을 계속 가지고 있다.
const student = useRef(new Student());
useRef
가 이전 값을 가지고 있어도, 컴포넌트가 리렌더링 될 때마다 new Student()
를 호출하긴 한다.
따라서 이 생성자 함수가 큰 작업일 경우 쓰지도 않을 것을 매번 호출하는 건 너무 낭비일 수 있다.
다음과 같이 회피할 수 있다.
function School() {
const student = useRef(null);
if (student.current === null) {
student.current = new Student();
}
}
컴포넌트가 렌더링 중에는
ref
의 값을 읽거나 쓰지 마라!
모든 리액트 컴포넌트는 순수 함수처럼 동작하기를 원한다. 즉, 입력 값이 같으면 출력 값도 같아야 한다.
하지만 중간에 ref
를 읽거나 쓰는 것은 이러한 기대를 깨버릴 수 있다.
function MyComponent() {
// ...
// 🚩 렌더링 중에 ref의 값을 변경하지 마시오!!
myRef.current = 123;
// ...
// 🚩 렌더링 중에 ref의 읽지도 마시오!!
return <h1>{myOtherRef.current}</h1>;
}
useEffect(() => {
// 컴포넌트가 렌더링이 전부 끝나고 바꾸거나,
myRef.current = 123;
});
const handleChange = () => {
// 변화 하는 이벤트를 handler에게 위임해라
ref.current = 2;
};
return <button onClick={handleChange}>change</button>
useRef
로 생성한 객체를 HTML요소에 ref
속성으로 주면, 객체가 요소와 매핑된다.
ref.current
에는 HTMLElement
요소가 들어가게 된다.
const inputRef = useRef();
return (
<div>
<input ref={inputRef} />
<button onClick={() => inputRef.current.focus()} />
</div>
)