이 글은 단순히 리액트 공식 문서를 읽고 베꼈을 뿐이다!! 학습 목적이라면 공식 문서를 보는 걸 추천한다.

공식 문서
https://react.dev/learn/manipulating-the-dom-with-refs
Manipulating the DOM with Refs – React
The library for web and native user interfaces
react.dev
노드의 ref를 얻기
import { useRef } from 'react';
const myRef = useRef(null);
<div ref={myRef}>
- 이벤트 핸들러 등에서
myRef.current
를 이용해 DOM node에 접근하여scrollIntoView()
,focus()
같은 브라우저 내장 api 사용 가능 - 배열의 map을 돌아서 나온 JSX에 ref를 부여하는 등의 작업이 필요할 때
querySelectorAll
같은 메서드로 한번에 찾아서 넣기: 불안정함. DOM이 바뀌면 망가질 수 있음- ref callback 만들기: DOM에
ref={myRef}
가 아니라ref={(node) => setNodeToMap(node, id)}
이런 식으로 하면 DOM ref를 설정할 시기에 해당 함수를 이용함
다른 컴포넌트의 DOM node에 접근하기
- ref를 DOM에 먹이는 건 브라우저의 built-in element들만 가능함
- 내가 만든 컴포넌트에 바로 ref를 먹이면 null이 나옴
- 기본적으로 리액트는 컴포넌트에서 다른 컴포넌트의 DOM node 접근을 불허함
- ref는 비상탈출구: 아꼈다가 정말정말 필요한 곳에 써야 함
- 본인의 DOM node에 대한 외부의 접근을 허용하려면 명시적으로 표시해야 함:
forwardRef
사용이런 식으로 '나 ref 받아요' 라고 광고해야 한다. const MyInput = forwardRef((props, ref) => { return <input {...props} ref={ref} />; });
useImperativeHandle
로 DOM 조작의 일부만을 허용하기
forwardRef
로 본인의 DOM node를 공개하면 밖에서 접근이 가능하다. 이 말인즉슨 단순히 포커싱이나 스크롤만이 아니라 입력창 내부의 값이나 css도 조작할 수 있다는 뜻이다!
리액트에서 제공하는 useImperativeHandle
을 이용하면 공개된 ref의 기능들을 제한할 수 있다.
const MyInput = forwardRef((props, ref) => {
const realInputRef = useRef(null);
useImperativeHandle(ref, () => ({
focus() {
realInputRef.current.focus();
},
}));
return <input {...props} ref={realInputRef} />;
});
이러면 외부에서는 focus()
밖에 사용할 수 없게 된다.
리액트가 ref에 값을 할당하는 순간
- 리액트에서 모든 업데이트는 두 단계로 나눌 수 있음
- render: 컴포넌트를 호출해 JSX 트리를 받아옴
- commit: UI 트리의 변화를 DOM에 반영함
- 일반적으로 ref는 렌더링 과정에서 사용하지 않음
- 따라서 맨 처음 렌더링에는 당연히 DOM이 아직 없으므로
ref.current
가 null - 리렌더링에서는 아직 DOM node가 업데이트되지 않았으므로
ref.current
를 읽기에는 너무 빠른 타이밍
- 따라서 맨 처음 렌더링에는 당연히 DOM이 아직 없으므로
- 리액트는
ref.current
값을 commit 단계에서 설정함: 업데이트 전에는 null로 하고 직후에 상응하는 DOM node로 바꿈 - 일반적으로 ref는 이벤트 핸들러에서 접근하게 됨: 이벤트는 딱히 없는데 ref를 건드리고 싶다면 effect로 가능(다음 장에 자세히 나옴)
flushSync
를 이용해서 동기적으로 DOM 업데이트하기
- 어떤 배열 상태에 map을 이용해 DOM 목록들을 만들고 거기에 대한 ref를 얻는다고 치자
- 우리가 해당 배열 상태를 변경하면 (상태 변경은 비동기적이므로) 즉각적으로 ref에 변경된 배열을 이용한 DOM이 담기지 않음
- 이럴 때
flushSync
를 사용하면 flushSync 내부의 콜백 함수 실행이 끝나자마자 DOM을 업데이트하도록 함 - 따라서 flushSync 이후에 ref를 접근하면 새로 바뀐 DOM을 이용할 수 있음
ref를 이용한 DOM 조작 맛있게 하기
- ref는 비상탈출구: '리액트 외부의 기능이 필요할 때'만 이용하기
- 파괴적이지 않은 기능을 이용할 때는 괜찮음: 스크롤, 포커스, ...
- 하지만 이걸로 DOM을 조작하려고 하면 리액트의 DOM 조작과 충돌 위험성이 있음
- 리액트가 관리하는 DOM을 조작하지 말기
- 물론 리액트가 절대 건드리지 않는 DOM은 조작해도 됨
'리액트 공식 문서 읽기' 카테고리의 다른 글
26화: You Might Not Need an Effect (0) | 2023.06.22 |
---|---|
25화: Synchronizing with Effects (0) | 2023.06.20 |
23화: Referencing Values with Refs (0) | 2023.06.19 |
22화: Scaling Up with Reducer and Context (0) | 2023.06.18 |
21화: Passing Data Deeply with Context (0) | 2023.06.18 |