# useCallbackRef

```tsx
type Ref<T> =
  | RefCallback<T>
  | RefObject<T>
  | null;
```

* `useRef`가 반환한 `ref`는 내용이 변경(`ref.current = value`)되더라도 **리렌더링을 발생시키지 않는 특징**을 갖는다.
  * JSX Element(ex. `<div>`)에 `ref`를 전달하면 컴포넌트가 렌더링된 이후 해당 DOM 노드에 대한 참조를 `ref.current`에 할당해주는 역할을 수행한다.
  * 만약 해당 요소가 unmount된다면 `ref.current`에 할당된 값을 `null`로 변경해주는 동작을 수행한다.

```tsx
<input
  ref={(node) => { 
    ref.current = node; 
  }}
/>
```

* 즉, `Ref`의 타입에서도 알 수 있듯이 기본적으로는 `RefObject`를 전달한 경우 위와 같은 **문법적 설탕(Syntatic Sugar)**&#xC744; 제공한다고 생각할 수 있다.
* 이때 리액트는 동일한 참조를 갖는 `ref`를 전달한 경우 `ref`에 전달한 함수를 다시 실행하지 않게된다.

```tsx
const ref = useCallback((node) => {
  node.focus();
}, []);

<input ref={ref} />
```

* 따라서 `useCallback`에 빈 의존성 배열(deps)을 전달해 만든 `RefCallback`을 전달하게 된다면 **DOM 노드가 렌더링된 이후에 최초 한 번만 실행되게끔 구현하는 것이 가능**하다.
* 일반적이지는 않지만 `ref.current`가 변경될 때마다 특정한 동작을 수행하게끔 하고 싶다면 `useCallbackRef`를 사용하면 된다.
  * 이전 값과 새로운 값이 동일하다면 전달한 `callback`은 실행되지 않는다.
  * `useRef` 대신 `useState`를 이용해 생성한 상태(ref)를 활용하므로 의존성 배열에 전달하면 변경될 때마다 반응하게끔 할 수 있게 된다.

```tsx
import { useCallbackRef } from "use-callback-ref";
import { useRef, useState } from "react";

const Component = () => {
  const [, forceUpdate] = useState();
  // 1. ref의 변경에 반응할 필요가 없는 경우
  const ref = useRef(null);
 
  useEffect(() => {
    // ref가 변경되더라도 실행되지 않음.
  }, [ref.current]);

  // 2. ref의 변경에 따라 반응해야 하는 경우
  const callbackRef = useCallbackRef(null, () => forceUpdate());

  useEffect(() => {
    // callbackRef가 변경되면 실행됨.
  }, [callbackRef.current]);
};
```

### Implementation

```tsx
import type { MutableRefObject } from "react";
import { useState } from "react";

/**
 * creates a MutableRef with ref change callback
 *
 * @example
 * const ref = useCallbackRef(0, (newValue, oldValue) => console.log(`${oldValue} -> ${newValue}`));
 * ref.current = 1;
 * // 0 -> 1
 */
function useCallbackRef<T>(
  initialValue: T | null,
  callback: (newValue: T | null, lastValue: T | null) => void
): MutableRefObject<T | null> {
  const [ref] = useState(() => ({
    // value
    value: initialValue,
    // last callback
    callback,
    facade: {
      get current() {
        return ref.value;
      },
      set current(value) {
        const last = ref.value;

        if (!Object.is(last, value)) {
          ref.value = value;
          ref.callback(value, last);
        }
      }
    }
  }));
  // update callback
  ref.callback = callback;

  return ref.facade;
}
```

### 참고 자료

* [The same useRef, but it will callback 🤙](https://dev.to/thekashey/the-same-useref-but-it-will-callback-8bo)
* <https://github.com/theKashey/use-callback-ref>


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://baek2back.gitbook.io/fundamental/hooks/usecallbackref.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
