Ref?
Ref는 render 메서드에서 생성된 DOM 노드나 React 엘리먼트에 접근하는 방법을 제공합니다
When you want a component to “remember” some information, but you don’t want that information to trigger new renders, you can use a ref
ref를 생성하고 console.log(ref)를 해보면 위와 같은 결과를 확인할 수 있다. ref는 다음과 같이 current property를 가진 객체이다. state와 다르게 refs는 그냥 javascript 객체이므로 값을 읽고 변경할 수 있다. React는 이 객체를 통해 DOM에 직접적으로 접근 할 수 있게 해준다.
ref가 DOM을 다룰 때 많이 사용되지만 DOM에만 사용되는 것은 아니다. timerId와 같은 값을 저장할 때도 사용될 수 있다. ref값을 변경되어도 재랜더링이 되지 않는 state로 생각해도 좋다.
ref는 컴포넌트에 변경 가능한 정보를 저장하는 비밀 주머니같은 존재이다. 재랜더링 없이 비밀스럽게 값을 가지고 있을 수 있다.
언제 사용?
- 포커스, 텍스트 선택영역, 혹은 미디어의 재생을 관리할 때.
- 애니메이션을 직접적으로 실행시킬 때.
- 서드 파티 DOM 라이브러리를 React와 같이 사용할 때.
Ref 접근
- ref 어트리뷰트가 HTML 엘리먼트에 쓰였다면, 생성자에서 React.createRef()로 생성된 ref는 자신을 전달받은 DOM 엘리먼트를 current 프로퍼티의 값으로서 받는다.
- ref 어트리뷰트가 커스텀 클래스 컴포넌트에 쓰였다면, ref 객체는 마운트된 컴포넌트의 인스턴스를 current 프로퍼티의 값으로서 받는다
- 함수 컴포넌트는 인스턴스가 없기 때문에 함수 컴포넌트에 ref 어트리뷰트를 사용할 수 없다.
왜 객체 안에 current를 가지고 있을까
공부를 하면서 궁금했다. ref에 그냥 값을 바로 가지고 있으면 되지 않나? current를 따로 둔 이유가 있을까? 결론적으로도 유동적인 DOM을 잘 적용하기 위해서이다
이를 알아보기 위해서는 Closure와 값/참조를 이해해야 한다.
function add(firstNumber) {
/* 함수가 생성될 때 firstNumber를 기억하게 된다 🤯 */
return function(secondNumber) {
return firstNumber + secondNumber
}
}
/* example 1 */
let dynamicNumber = 10;
let addDynamicNumber = add(dynamicNumber);
addDynamicNumber(15); // 25
/* example 2 */
dynamicNumber = 20;
addDynamicNumber(15) // 25 원하던 결과가 아니다!!
위는 stale한 closure 예시이다(stale: 오래된, 신선하지 않은) 만약 example2를 원하는대로 dynamicNumber를 설정하려면 어떻게 해야할까? 위의 예시는 값을 변경하여 나타난 결과다.
말이 애매해서 아래 예시로 대체한다. 아래 예시처럼 firstNumber는 dynamicNumber를 복사하였기 때문에 별도의 값이 된다.
let x = 1;
let y = x;
x = 2;
console.log(x); //2
console.log(y); //1
이를 해결하려면 객체를 사용하면 된다. 객체는 참조에 의한 복사가 되기 때문에 복사 시 동일한 참조를 갖게 된다.
function add(firstNumber) {
return function(secondNumber) {
/* firstNumber current를 key로 가지는 객체! */
return firstNumber.current + secondNumber
}
}
let dynamicNumber = { current: 10, };
let addDynamicNumber = add(dynamicNumber);
dynamicNumber.current = 20;
addDynamicNumber(15) // 35! 원하는대로 동작한다 😆
공식 문서의 문구를 다시 보자
컴포넌트가 마운트될 때 React는 current 프로퍼티에 DOM 엘리먼트를 대입하고, 컴포넌트의 마운트가 해제될 때 current 프로퍼티를 다시 null로 돌려 놓습니다. ref를 수정하는 작업은 componentDidMount 또는 componentDidUpdate 생명주기 메서드가 호출되기 전에 이루어집니다.
실제 DOM에 React 노드가 렌더될 때까지 ref가 가리키는 DOM 요소의 주소 값이 확정되지 않는다. ref에 접근할 수 있는 시점은 React 노드가 실제로 DOM에 반영되는 시점부터이다. 또한 DOM이 업데이트 되는 경우(componentDidUpdate)에도 실제 DOM이 변경될 수 있어 ref가 변경되게 된다.
이처럼 DOM은 유동적이기때문에 React는 객체를 반환해 current 프로퍼티의 값을 계속해서 수정한다! 변경이 계속해서 가능하도록 ref는 객체로 이루어졌다.
ForwardRef
import { useRef } from 'react';
function MyInput(props) {
return <input {...props} />;
}
export default function MyForm() {
const inputRef = useRef(null);
function handleClick() {
console.log(inputRef)
}
return (
<>
<MyInput ref={inputRef} />
<button onClick={handleClick}>
Focus the input
</button>
</>
);
}
커스텀 컴포넌트에 ref를 달게 되면 이 컴포넌트의 인스턴스를 ref로 가지게 된다. 만약 위 예시가 클래스형이였다면 버튼 클릭 시 {current: null} 가 출력된다. 위에서 언급했듯이 함수 컴포넌트는 인스턴스가 없기 때문에 함수 컴포넌트에 ref 어트리뷰트를 사용할 수 없다.
import { useRef, useEffect } from "react";
function MyInput() {
const ref = useRef(null);
useEffect(() => {
console.log(ref);
}, [ref]);
return (
<div className="App">
<StyledInput ref={ref} />
</div>
);
}
const StyledInput = ({ ref }) => {
return <input ref={ref} />;
};
export default MyInput;
ref값은 key와 같이 props로 전달할 수 없다.
Most props on a JSX element are passed on to the component, however, there are two special props (ref and key) which are used by React, and are thus not forwarded to the component.
이럴때는 부모 컴포넌트가 자식들에게 ref를 forward(전달하기)하면 된다.
const MyInput = forwardRef((props, ref) => {
return <input {...props} ref={ref} />;
});
- <MyInput ref={inputRef} /> tells React to put the corresponding DOM node into inputRef.current. However, it’s up to the MyInput component to opt into that—by default, it doesn’t.
- The MyInput component is declared using forwardRef. This opts it into receiving the inputRef from above as the second ref argument which is declared after props.
- MyInput itself passes the ref it received to the <input> inside of it.
forwardRef 없이도 별도의 prop으로 ref를 전달해 줄 수 있다. 하지만 ref로 전달해주게 된다면 내부를 보지 않고도 쉽게 코드를 이해할 수 있다.
참조
https://ko.reactjs.org/docs/refs-and-the-dom.html
https://tecoble.techcourse.co.kr/post/2021-05-15-react-ref/
https://tkplaceholder.io/why-do-refs-have-a-key-named-current/
https://ko.reactjs.org/docs/forwarding-refs.html#forwarding-refs-to-dom-components
https://reactjs.org/warnings/special-props.html
https://beta.reactjs.org/learn/referencing-values-with-refs
https://beta.reactjs.org/learn/manipulating-the-dom-with-refs
'React' 카테고리의 다른 글
[React] 함수형 컴포넌트는 렌더링된 값들을 고정시킨다 (1) | 2022.09.14 |
---|---|
[React] Virtual DOM and Rendering (2) | 2022.05.29 |
[React] Props, State (0) | 2021.11.12 |
[React] JSX? (0) | 2021.09.02 |