본문 바로가기
프로그래밍/React

[React] useRef

by YuminK 2023. 11. 28.

useRef

useRef is a React Hook that lets you reference a value that’s not needed for rendering.

 

const ref = useRef(initialValue)

 

Parameters 

initialValue: The value you want the ref object’s current property to be initially. It can be a value of any type. This argument is ignored after the initial render.

 

Returns 

useRef returns an object with a single property:

 

current: Initially, it’s set to the initialValue you have passed. You can later set it to something else. If you pass the ref object to React as a ref attribute to a JSX node, React will set its current property.

On the next renders, useRef will return the same object.

 

값을 바꿔도 다시 렌더링하지 않는다. ref는 단순한 자바스크립트 오브젝트로 처리된다. 렌더링하는 도중에 ref.current를 읽거나 쓰지 마라 (초기화 제외) 이러한 행위는 컴포넌트의 동작을 예측할 수 없게 한다. StrictMode에서 2번씩 호출된다. pure한 함수를 사용하고 있다면 문제가 표시되지 않는다. 

 

컴포넌트의 시각적 출력에 영향을 주지 않을 데이터를 저장하기 위해 적합하다. 

 

function handleStartClick() {

  const intervalId = setInterval(() => {

    // ...

  }, 1000);

  intervalRef.current = intervalId;

}

 

function handleStopClick() {

  const intervalId = intervalRef.current;

  clearInterval(intervalId);

}

 

ref를 사용하여, 리런더링 사이에 정보를 저장한다. (일반적인 변수는 매 렌더링시 재설정된다.)

값을 변경하는 것은 렌더링을 트리거 하지 않는다. (state 변수는 재렌더를 트리거한다.)

해당 정보는 각 컴포넌트의 copy 마다 로컬하게 처리된다. (컴포넌트 외부 변수와는 달리)

 

화면에 띄워야 하는 목적으로 데이터를 저장하기에는 적합하지 않다. 

 

flutter의 provider의 경우, 비즈니스 로직과 뷰의 분리를 위해 provider쪽에 여러 변수를 선언할 수 있다. 만약 해당 변수가 렌더링에 사용되지 않을지라도 그냥 선언해서 사용한다. 근데 리액트에서는 표츌되어야 하는 변수(useState)와 그렇지 않은 변수(useRef)로 나눠서 처리하고 있다.

 

ref.current 값은 렌더링 중에 읽거나 쓰지 마세요. 입력이 같다면 같은 jsx가 결과로 표출되어야 한다. 다른 순서와 다른 인자로 호출해도 다른 콜에 대한 결과에 영향을 주어서는 안 된다. 렌더링 도중에 ref.current값을 읽고 쓰는 것은 이러한 기대를 부시는 것이다.

 

function MyComponent() {

  // ...

  // 🚩 Don't write a ref during rendering

  myRef.current = 123;

  // ...

  // 🚩 Don't read a ref during rendering

  return <h1>{myOtherRef.current}</h1>;

}

 

이벤트 핸들러나 이펙트 내에서 사용해라.

 

function MyComponent() {

  // ...

  useEffect(() => {

    // ✅ You can read or write refs in effects

    myRef.current = 123;

  });

  // ...

  function handleClick() {

    // ✅ You can read or write refs in event handlers

    doSomething(myOtherRef.current);

  }

  // ...

}

 

만약 렌더링 도중에 값을 읽거나 써야 한다면, state를 대신 사용하면 된다. 이러한 룰을 부신다면, 리액트의 신규 기능들이 동작하지 않을 가능성이 있다고 한다.

 

특히 DOM을 조작하기 위해 ref를 사용하는 것이 일반적이다. 

  const inputRef = useRef(null);

 

useRef(null)로 선언한 다음에 DOM객체에 ref속성에 넣어라. 그리고 핸들러에서 사용하면 된다. 

 

 const playerRef = useRef(new VideoPlayer());

 

리액트는 useRef의 ref 값을 저장하고 다음 렌더에서 처리하지 않는다. 그러나 해당 값은 계속 처리된다. 

new VidioPlayer()는 1번 초기화 되지만, 이후에도 계속 생성되는 것이다. 

 

  const playerRef = useRef(null);

  if (playerRef.current === null) {

    playerRef.current = new VideoPlayer();

  }

 

이런 식으로 문제를 해결할 수 있다. 렌더링 도중에 값을 읽는 것이지만 상태가 초기화 중에 1번 처리되므로 완전히 예측 가능하다.

 

function Video() {

  const playerRef = useRef(null);

 

  function getPlayer() {

    if (playerRef.current !== null) {

      return playerRef.current;

    }

    const player = new VideoPlayer();

    playerRef.current = player;

    return player;

  }

 

  // ...

  

항상 nullcheck를 하고 싶지 않은 경우에는 따로 함수를 빼서 이벤트 핸들러에서 값을 가져오도록 처리할 수 있다. 

 

const inputRef = useRef(null);

 

return <MyInput ref={inputRef} />;

 

Warning: Function components cannot be given refs. Attempts to access this ref will fail. Did you mean to use React.forwardRef()?

 

커스텀 컴포넌트를 사용하는 도중에 문제가 발생한다면, forwardRef를 사용하여 처리할 수 있다. 

 

export default function MyInput({ value, onChange }) {

  return (

    <input

      value={value}

      onChange={onChange}

    />

  );

}

 

이렇게 된 것을 다음처럼 수정한다.

 

import { forwardRef } from 'react';

 

const MyInput = forwardRef(({ value, onChange }, ref) => {

  return (

    <input

      value={value}

      onChange={onChange}

      ref={ref}

    />

  );

});

 

export default MyInput;

 

https://react.dev/reference/react/useRef

'프로그래밍 > React' 카테고리의 다른 글

[React] useMemo  (0) 2023.11.29
[React] useCallback  (1) 2023.11.28
[React] useEffect  (0) 2023.11.28
[React] useState  (0) 2023.11.27
[React] 리액트 개요  (0) 2023.11.27

댓글