lighthouse.log

lighthouse.log

React의 useRef가 다른 hooks들과 다른점

2021-08-15

useRef?

  • 지금까지 DOM에 접근하기 위해서 Ref를 사용해왔다.
  • React16.8 부터 hooks 등장과 함께 useRef() 가 공개되었다.
  • 최근에 알게 되었지만 useRef hooks는 이전버전의 createRef를 단순히 hooks로 처리한 것이 아니라고 한다.

이전 버전의 createRef를 단순히 hooks으로 처리한것이 아니다

  • 즉, 클래스 컴포넌트에서의 createRef()useRef()는 다르다.
  • react-redux 의 useSelector 소스코드에서 useRef 를 적극적으로 사용하고 있다고 한다.
  • 공식문서에 설명된 DOM노드나 React 엘리먼트에 접근하기 위한 방법이 아니라 일종의 변수를 관리하기 위한 목적으로 사용하고 있다.

useRef()는 어떤 값이든 저장할 수 있는 일반적인 JS객체이다

useRef()는 DOM 뿐만 아니라 어떤 값이든 저장할 수 있는 일반적인 JS객체이다.

  1. useRef() 는 일반적인 JS 객체이다.

    • 즉 heap 영역에 저장되는 변수이다.
    • 실제로 useRef를 코드로 확인해보면 순수 자바스크립트 객체(plain object)로 만들어진 hooks이다.
  2. 매번 렌더링할 때 동일한 객체를 제공한다

    • heap에 저장되어 있기 때문에 어플리케이션이 종료되거나 가비지 컬렉팅될 때까지 참조할 때마다 같은 메모리 값을 가진다고 할 수 있다.
  3. 값이 변경되어도 리렌더링이 발생하지 않는다.

    • 같은 메모리 주소를 가지고 있으므로 JS의 === 연산이 항상 true를 반환한다.
    • 즉, 변경사항을 감지할 수 없어서 리렌더링 하지 않는다는 의미이다.
    • 만약에 DOM에 ref를 더하거나 제거할 때 어떤 코드를 실행시키고 싶다면 useRef가 아니라 useCallback를 사용하라고 한다.

createRef가 잘 동작하는데 왜 useRef를 만들었을까

  • 함수형 컴포넌트는 인스턴스를 리턴하는 클래스 컴포넌트와는 조금 다르게 동작한다.

    • 렌더링 될 때마다 매번 새로운 변수를 스택에 할당해 값이 초기화되기도 하고
    • 불필요한 성능 낭비를 하게될 수 있다.
  • 클래스 컴포넌트는 인스턴스를 생성해서 렌더링 메소드만 재실행하는 구조였다면
  • 함수형 컴포넌트는 매번 함수(=함수형 컴포넌트의 렌더링)를 실행하기 때문이다.
  • useRef는 함수형 컴포넌트에서 변수를 다루기 쉽게 하기위해 (마치 클래스의 인스턴스 변수처럼) 만들어진 API이다.

class의 인스턴스 프로퍼티처럼 사용가능하다

useRef는 React 공식문서에도 나와있듯이 인스턴수 변수와 같이 사용가능하다.

인스턴스 변수와 같은 것이 있습니까?

네! useRef() Hook은 DOM ref만을 위한 것이 아닙니다. “ref” 객체는 현재 프로퍼티가 변경할 수 있고 어떤 값이든 보유할 수 있는 일반 컨테이너입니다. 이는 class의 인스턴스 프로퍼티와 유사합니다.

다른 변수 선언 방법과의 차이

1. hooks 기반의 useState 혹은 useContext로 선언

  • 이렇게 선언한 변수들은 값이 변경될 때마다 리렌더링이 발생.
  • 렌더링과 상관없이 변수를 선언하기에 적합하지 않다.

2. 함수형 컴포넌트 내부에 const 혹은 let, var로 선언

  • 렌더링 될 때마다 값이 초기화된다.
  • 컴포넌트의 생애주기 동안 관리해야하는 변수를 선언하기에 적합하지 않다.

3. 컴포넌트 바깥에 const 혹은 let, var로 선언

  • 불필요한 렌더링을 유발하지도 않고 렌더링 될 때 초기화 되지도 않는다.
  • 하지만 컴포넌트를 재사용하면서 값을 각각 따로 관리하는것이 불가능하다.
let componentId = null;

function MyComponent(props) {
  componentId = props.id;
  return <h1>This is a component.</h1>;
}
// 이 경우 MyComponent를 여러번 재사용하더라도 componentId 는 어플리케이션 내에 단1개만 존재한다.

4. useRef를 이용한 선언

  • useRef를 통해 선언된 변수는 리렌더링을 유발하지 않으며
  • 리렌더링 될 때도 이전의 값을 기억하고 있으며
  • 컴포넌트마다 각각의 값을 가질 수 있다.

결론

  • useRef는 클래스의 인스턴스 프로퍼티와 같다고 생각하면 된다.
  • 컴포넌트 내부에서 관리하는 변수인데, 값아 바뀔때마다 렌더링이 필요하면 useState를 쓰면 되고 아닐 경우 useRef를 써야한다고 생각하면 간단하다.

참고 URL