😉forwardRef는 무엇?
https://react.dev/reference/react/forwardRef
공식문서에서 forwardRef를 찾아볼 수는 있지만 아마 리액트를 가볍게 사용법만 익히신 분들은
낯선 함수일것이라고 생각합니다.
forwardRef는 참조를 사용하여 부모 컴포넌트에 DOM 노드를 노출시키는 함수입니다.
조금 다르게 이야기하면
forwardRef는 부모컴포넌트가 자식컴포넌트에게 Ref를 전달해야할 때 사용하는 함수입니다.
const SomeComponent = forwardRef(render)
따라서 이 forwardRef를 이해하기 위해서는 Ref에 대한 선수지식이 필요합니다.
😙Ref는 뭔가요?
Ref는 createRef, useref 두가지의 방법으로 생성할 수 있습니다.
createRef의 경우에는 클래스형 컴포넌트에서 사용하던 방식이며
useRef는 함수형 컴포넌트에서 사용하기 위해 "훅"으로 만들어준 형태라고 생각해주시면 되겠습니다.
편의상 useRef를 중심으로 서술하도록 하겠습니다.
간단하게 설명하면 Ref는 그냥 "자바스크립트 객체"라고 설명을 퉁칠 수 있습니다.
혹은 마치 자바스크립트의 document.querySelector('')와 같은 일을 하는 훅이다.라고도 설명을 퉁칠 수 있습니다.
혼란스러울 수 있지만 사실입니다. 왜냐하면 useRef에는 두가지 use case가 있거든요!
이에 대해서는 차근차근 서술하겠습니다.
그런데 그것을 알아보기 이전에 이 Ref는 특이한 동작 방식을 가지고 있습니다.
자세한 정보는 위 포스트에서 아주 잘 설명해주고 있으니 제가 따로 설명하지는 않겠습니다.
하지만 정말 중요하고 재미있는 내용이니 나중에 시간에 여유가 되실때는 꼭 읽어보시길 바랍니다.
https://react.dev/learn/referencing-values-with-refs
다음은 리액트 공식 문서에서 발췌한 refs와 state의 차이점에 대한 표입니다.
실전적인 사용법에 대해서는 이 표에 중요한 부분들이 모두 담겨있습니다.
간단하게 요약하면 다음과 같은 내용입니다.
ref는
1. useRef("엄준식")을 하는 경우 useRef() 함수의 반환값은 { current : "엄준식" }이다.
2. useRef의 값은 변경되어도 리렌더링 되지 않는다.
3. useRef는 mutable하다 즉 변경이 되어도 된다.
4. 렌더링 중에는 useRef의 값을 쓰거나 읽을 수 없다.
라는 규칙을 가지고 있습니다.
여기서 주의하시는 점은 useRef의 반환값의 current 키값은 사전에 약속된 값입니다.
그러니 사용을 하실때는 .current의 형태로 접근하면 내가 initialState에 넣어준 값에 접근이 되는거에요!
그리고 이러한 특징을 기반하여 useRef는 대표적으로 두가지 use case를 가질 수 있습니다.
useRef의 두가지 use Case
사실 당연하게도 더 많은 use case가 있을 수 있지만 대표적으로 많이 사용되는
use Case는 두가지라고 생각하여 두가지로 서술합니다.
1. HTML 요소를 셀렉팅할 수 있습니다.
import { useRef } from 'react';
const App = () => {
const inputRef = useRef();
const focus = () => {
console.log(inputRef.current);
};
console.log(inputRef.current);
return (
<div>
<input type="text" ref={inputRef} />
<button onClick={focus}>포커싱</button>
</div>
);
};
export default App;
위 코드를 작성하고 실행시킨 뒤 버튼을 눌러보면 다음과 같은 결과가 출력됩니다.
처음에는 useRef의 매개변수로 아무것도 넣어주지 않았기에 inputRef의 current값이 undefined입니다.
input 태그의 ref 어트리뷰트에 inputRef를 전달해주었습니다.
이때 ref 어트리뷰트는 따로 설정하는 것이 아니라 사전에 약속된 값입니다.
그리고 버튼을 클릭하여 inputRef의 값을 다시한번 console에 찍어보면
마치 document.querySelector를 한것처럼 html 태그가 셀렉팅된것을 확인할 수 있습니다!
이렇듯 useRef는 실제 돔을 직접적으로 조작해야할 일이 있을 때 사용할 수 있습니다.
2. useRef는 리렌더링이 되지 않기에 화면에 표시되지 않는 변수를 관리하는 데 사용할 수 있다.
import { useRef } from 'react';
const App = () => {
const inputRef = useRef(0);
const increase = () => {
inputRef.current += 1;
console.log(inputRef);
};
console.log(inputRef.current);
return (
<div>
<h2>{inputRef.current}</h2>
<input type="text" />
<button onClick={increase}>올려요</button>
</div>
);
};
export default App;
위 코드와 같이 useRef는 값이 변해도 리렌더링이 되지 않는 특성이 있기때문에
화면에 보여질 필요는 없지만 자주 변경되어야하는 값을 저장하고자 할 때
useState보다 좋은 선택지가 될 수 있습니다.
😉forwardRef의 사용방법
그런데 리액트에 어느정도 익숙하신 분들은 이런 생각이 들 수 있습니다.
아니 부모에서 자식으로 뭔가를 넘겨주고 싶은거면 props로 넘겨주면 되는거아니야?
근데 그게 안됩니다..
import React from 'react';
const MyInput = (props) => {
console.log(props);
return (
<>
<input ref={props.inpurRef} type="text" />;
<button onClick={() => console.log(props.inpurRef)}>내 Ref는..??</button>
</>
);
};
export default MyInput;
이렇게하면 되는 것 아닐까요??
네.. 아닙니다.
실제로 해보게 되면
props를 받아올때까지만해도 괜찮았던 inputRef는
html 요소의 ref로 달아주고 나니 undefined가 되어버립니다.
forwardRef는 바로 이런 상황에서 필요합니다.
import { useRef } from 'react';
import MyInput from './MyInput';
const App = () => {
const inputRef = useRef();
const increase = () => {};
return (
<div>
<MyInput ref={inputRef} />
<button onClick={increase}>올려요</button>
</div>
);
};
export default App;
이번에는 ref에다가 값을 넣어줘보겠습니다.
이렇게하면 개발자 도구의 콘솔창에 경고메시지가 뜨게되는데 무슨 내용 인지는 직접 해보시길 바랍니다.
import React, { forwardRef } from 'react';
const MyInput = (props, ref) => {
console.log(props);
return (
<>
<input ref={ref} type="text" />;
<button onClick={() => console.log(ref)}>내 Ref는..??</button>
</>
);
};
export default forwardRef(MyInput);
그리고 MyInput 컴포넌트를 forwardRef로 감싸줍니다.
또한 props 외에도 ref라는 매개변수를 추가로 받아오도록합니다.
이것은 우리가 특별한 키값 ref에 넣어준 값이 넘겨지게 됩니다.
따라서 부모컴포넌트에서는 자식컴포넌트에게 ref={만든 useRef반환값}의 형태로 값을 전달합니다.
그런 뒤 ref를 자식컴포넌트에 전달해보면 이제는 정상적으로 ref를 사용할 수 있음을 알 수 있습니다!
부록 : useRef는 어째서 값이 바뀌어도 리렌더링되지 않는건가요?
useRef는 current 프로퍼티를 갖고 있는 객체를 반환합니다.
즉 useRef는 자바스크립트의 객체이며 자바스크립트의 객체는 힙 영역에 저장됩니다.
그리고 이 객체는 리렌더링이 될 때에도 같은 메모리 주소를 유지합니다.
같은 메모리 주소를 갖고 있는 객체 둘을 서로 비교하게되면 그 결과는 true입니다.
따라서 useRef는 값이 변해도 메모리 주소는 같기 때문에 내부 값의 변경을 리액트가 감지할 수 없습니다.
'react' 카테고리의 다른 글
[react-query] react-query의 query key는... (0) | 2023.06.06 |
---|---|
[react-query]@tanstack/react-query를 사용해보자 (1) | 2023.06.05 |
React.createPortal을 이용해 모달 만들기 (0) | 2023.05.15 |
React.lazy와 코드스플리팅 (0) | 2023.04.18 |
immer를 이용해 깊은 복사를 케이크처럼 간단히 먹자 (1) | 2023.04.08 |