일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | |||||
3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 |
- gulp
- error
- frontend
- VW
- animation
- html
- 이클립스
- ref
- next.js
- Eclipse Compare View
- Adobe
- Sass
- JavaScript
- Study
- CSS3
- tomcat
- React
- TaskRunner
- 이클립스 소스 비교 안보일 때
- ref전달하기
- SSR
- 정적웹사이트
- Eclipse
- Eclipse Bug
- npm
- 보일러플레이트
- 자바스크립트
- java
- 1분코딩
- css
- Today
- Total
프론트 개발 블로그
Ref 와 DOM 본문
Ref 란?
React 에서 컴포넌트에 접근할 수 있는 특수한 어트리뷰트를 말합니다.
React.createRef() 함수 / 콜백 함수 / 문자열(레거시 API) 로 생성할 수 있습니다.
ref 어트리뷰트가 콜백 함수인 경우, 함수는 DOM 엘리먼트나 Class 인스턴스를 인자로 받습니다.
Ref 사용
함수형 컴포넌트를 사용할 땐 : useRef() Hook API
클래스형 컴포넌트를 사용할 땐 : React.createRef() 메서드
React 에서 DOM 을 직접 선택 해야 할 떄, Ref 를 사용합니다.
- 특정 엘리먼트의 크기나 위치를 선택 해야 할 때
- 포커스 설정을 해야 할 때
- 스크롤 바의 위치나 설정 등
- 미디어의 재생을 관리 할 때 (VideoJS 등 HTML5 비디오 관련)
- 서드 파티 DOM 라이브러리를 React 와 같이 사용할 때 (D3, ChartJS 등 특정 DOM)
선언적으로 해결할 수 있는 문제에서는 ref 사용을 피해야 합니다.
예를 들어, Diglog 컴포넌트에서 open() 과 close() 메서드 대신에 isOpen이라는 prop 를 사용해서 제어합니다.
Ref 를 남용하지 마세요.
ref 는 애플리케이션에서 '어떤 일이 일어나게' 할 때 사용될 수도 있는데,
이런 경우 어느 컴포넌트 계층에서 상태를 소유해야 하는 지 생각해보아야 합니다.
대게 상태를 소유해야 하는 적절한 장소는 더 높은 계층이란는 결론이 나고,
상태를 상위 계층으로 올리는 'State 끌어올리기' 방법으로 ref 대신에 사용할 수 있습니다.
Ref 생성하기
ref 는 React.createRef() 를 통해 생성합니다.
ref 속성을 통해 React 엘리먼트에 부착합니다.
보통 컴포넌트의 인스턴스가 생성 될 때(constructor) ref 를 프로퍼티로 추가하고,
컴포넌트의 인스턴스의 어느 곳에서라도 ref 에 접근할 수 있게 해야 합니다.
* 함수 컴포넌트는 인스턴스가 없기 때문에 ref 어트리뷰트를 사용할 수 없습니다 (대신 useRef() Hook 사용하면 됨)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
import React, { Component } from 'react';
class RefStudy extends Component {
constructor(props) {
super(props);
this.myRef = React.createRef(); // ref 생성하기
}
render() {
return(
<div ref={this.myRef} /> // ref 어트리뷰티 속성을 통해 React 엘리먼트에 부착
)
}
}
export default RefStudy;
|
Ref 에 접근하기
render 메서드 안에서 ref 가 엘리먼트에 전달 되었을 때,
그 노드의 향한 참조는 ref 의 current 어트리뷰트에 담기게 됩니다.
1
|
const node = this.myRef.current;
|
ref 의 값은 노드 유형에 따라 다릅니다.
1. HTML 엘리먼트에서 쓰인 ref 어트리뷰트
: ref는 자신을 전달 받은 DOM 엘리먼트를 current 프로퍼티의 값으로 받습니다.
2. 커스텀 class 컴포넌트에서 쓰인 ref 어트리뷰트
: ref 객체는 마운트된 컴포넌트의 인스턴스를 current 프로퍼티 값으로 받습니다.
3. 함수 컴포넌트는 인스턴스가 없기 때문에 함수 컴포넌트에 ref 어트리뷰트를 사용할 수 없습니다.
DOM 엘리먼트에 Ref 사용하기
DOM 노드에 대한 참조를 저장하기 위한 ref 사용 예시 :
1. constructor 에서 textInput DOM 엘리먼트를 저장하기 위한 ref 를 생성합니다.
2. 리액트에게 input 엘리먼트를 우리가 생성자에서 생성한 ref 와 연결하고 싶다고 이야기 합니다.
3. DOM API 를 사용하여 명시적으로 input 엘리먼트에 포커스 합니다.
DOM 노드로 접근하기 위해 'current' 프로퍼티에 접근하고 있습니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
|
import React from 'react';
class CustomTextInput extends React.Component {
constructor(props) {
super(props);
// 1. textInput DOM 엘리먼트를 저장하기 위한 ref 를 생성합니다.
this.textInput = React.createRef();
this.focusTextInput = this.focusTextInput.bind(this);
}
focusTextInput() {
// 3. DOM API 를 사용하여 명시적으로 input 엘리먼트에 포커스합니다.
// DOM 노드로 접근하기 위해 'current' 프로퍼티에 접근하고 있습니다.
this.textInput.current.focus();
}
render() {
return(
<>
{/* 2. React 에게 input 엘리먼트를 우리가 생성자에서 생성한
ref 와 연결하고 싶다고 이야기 합니다 */}
<input
type="text"
ref={this.textInput}
/>
<input
type="button"
value="Focus the text input"
onClick={this.focusTextInput}
/>
</>
);
}
}
export default CustomTextInput;
|
마운트 될 때만 React는 current 프로퍼티에 DOM 엘리먼트에 대입하고,
마운트가 해제 될 때 current 프로퍼티를 다시 null 로 돌려놓습니다.
ref를 수정하는 작업은 componentDidMount 또는 componentDidUpdate 생명주기 메서드가
호출 되기 전에 이루어집니다.
1
2
3
|
constructor {current: null}
render {current: null}
focuseTextInput {current: input} // 마운트 시에만 DOM 엘리먼트에 대입함
|
클래스 컴포넌트에 Ref 사용하기
CustomTextInput 컴포넌트의 인스턴스가 마운트 된 이후에 input 에 즉시 포커스 되는 것을 흉내내기 위한 코드입니다.
ref 를 이용하여 CustomTextInput 컴포넌트의 인스턴스에 접근하고,
직접 focusTextInput 메서드를 호출 할 수 있습니다.
* 해당 코드는 CustomTextInput 컴포넌트가 클래스형 컴포넌트 일 때만 작동합니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
|
import React, { Component, createRef } from 'react';
import CustomTextInput from './CustomTextInput';
class AutoFocusTextInput extends Component {
constructor(props) {
super(props);
// 1. ref 생성 후
this.textInput = createRef();
}
componentDidMount() {
//3.CustomTextInput 컴포넌트의 인스턴스에 접근하고 직접 focusTextInput() 메서드 호출
this.textInput.current.focusTextInput();
}
render() {
return(
{/* 2. ref 를 연결 */}
<CustomTextInput ref={this.textInput} />
);
}
}
export default AutoFocusTextInput;
|
Ref 와 함수 컴포넌트
함수 컴포넌트는 인스턴스가 없기 때문에 함수 컴포넌트에 ref 어트리뷰트를 사용할 수 없습니다.
함수 컴포넌트에 ref 를 사용할 수 있도록 하려면,
forwardRef (높은 확률로 useImperativeHandle도 함께) 사용하거나 클래스형 컴포넌트로 사용해야 합니다.
다만, DOM 엘리먼트나 클래스 컴포넌트 인스턴스에 접근하기 위해
ref 어트리뷰트를 함수 컴포넌트에서 사용하는 것은 됩니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
function CustomTextInput(props) {
const textInput = useRef(null);
function handleClick() {
textInput.current.focus();
}
return(
<>
<input
type="text"
ref={textInput}
/>
<input
type="button"
value="focus the text input"
onClick={handleClick}
/>
</>
);
}
|
부모 컴포넌트에게 DOM ref 를 공개하기
보기 드문 경우지만 부모 컴포넌트에서 자식 컴포넌트의 DOM 노드에 접근하고자 할 때
1. 자식의 DOM 노드에 포커스
2. 크기 또는 계산 하는 작업 등
A. ref 전달하기 (React.forwardRef API) 를 통해서 사용할 수 있습니다.
* 자식 컴포넌트가 함수 컴포넌트인 경우 동작하지 않습니다.
React 16.3 이후 버전 : forwardRef API 사용가능
React 16.2 이전 버전 : forwardRef 보다 유연한 방법을 원한다면
https://gist.github.com/gaearon/1a018a023347fe1c2476073330cc5509
React 16.2 이전 버전에서 forwardRef 보다 유연한 방법을 원한다면?
React 16.3 이상으로 마이그레이션 할 수 없을 때 다음 접근 방식을 대신 사용할 수 있습니다.
자식에게 특별한 props 를 노출시킵니다. 이 props 는 ref 이외의 이름으로 지정할 수 있습니다.
그런 다음 자식 구성 요소는 prop을 ref 속성으로 DOM 노드에 전달할 수 있습니다.
이렇게 하면 부모가 중간에 있는 컴포넌트를 통해 자식의 DOM 노드에 참조를 전달할 수 있습니다.
클래스 컴포넌트, 함수형 컴포넌트에서 해당 기능은 모두 작동됩니다.
function CustomTextInput(props) {
return (
<div>
<input ref={props.inputRef} />
</div>
);
}
class Parent extends React.Component {
constructor(props) {
super(props);
this.inputElement = React.createRef();
}
render() {
return (
<CustomTextInput inputRef={this.inputElement} />
);
}
}
function CustomTextInput(props) {
return (
<div>
<input ref={props.inputRef} />
</div>
);
}
function Parent(props) {
return (
<div>
My input: <CustomTextInput inputRef={props.inputRef} />
</div>
);
}
class Grandparent extends React.Component {
constructor(props) {
super(props);
this.inputElement = React.createRef();
}
render() {
return (
<Parent inputRef={this.inputElement} />
);
}
}
가능하다면 DOM 노드를 외부에 공개하는 일은 피해야 합니다.
콜백 Ref
ref 가 설정되고 해제되는 상황을 세세하게 다룰 수 있는 콜백 ref 가 부르는 방법을 제공합니다.
콜백 ref 를 사용할 때에는 ref 어트리뷰트에 React.creatRef() 를 통해 생성된 ref 를 전달하는 대신, 함수를 전달합니다.
다른 곳에 저장되고 접근될 수 있는 React 컴포넌트의 인스턴스나 DOM 엘리먼트를 인자로서 받습니다.
1. 인스턴스가 마운트 될 때 React 는 ref 콜백을 DOM 엘리먼트와 함께 호출합니다.
2. 그리고 컴포넌트의 인스턴스의 마운트가 해제될 때 ref 콜백을 null 과 함께 호출합니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
|
import React, { Component } from 'react';
class CallbackRef extends Component {
constructor(props) {
super(props);
this.textInput = null;
this.setTextInputRef = (element) => {
console.log(element); // <input type="text" />
this.textInput = element;
}
this.focusTextInput = () => {
if(this.textInput) {
this.textInput.focus();
}
}
}
componentDidMount() {
this.focusTextInput();
}
render() {
return(
<>
<input
type="text"
ref={this.setTextInputRef}
/>
<input
type="button"
value="Focus the text input"
onClick={this.focusTextInput}
/>
</>
);
}
}
export default CallbackRef;
|
콜백 ref 또한 React.createRef() 로 생성했던 객체 ref 와 같이 다른 컴포넌트에 전달할 수 있습니다.
1. Parent 는 자신의 콜백 ref 를 props로서 CustomTextInput 에게 전달합니다.
2. CustomTextInput 은 전달받은 함수를 input 에게 ref 어트리뷰트로 전달합니다.
3. 결과적으로 Parent 에 있는 this.inputElement 는 CustomTextInput 의 <input> 엘리먼트에 대응하는 DOM 노드가 됩니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
class Parent extends React.Components {
render() {
return(
<CustomTextInput inputRef={el => this.inputElement = el} />
);
}
}
function CustomTextInput(props) {
return(
<>
<input ref={props.inputRef} />
</>
);
}
|
콜백 ref 에 대한 주의사항
ref 콜백이 인라인 함수로 선언되었다면 ?
ref 콜백은 업데이트 과정 중 처음에는 null 로, 그 다음에는 DOM 엘리먼트로 총 두 번 호출 됩니다.
이러한 과정은 매 렌더링 될 때마다 ref 콜백의 새 인스턴스가 생성되고, React 가 이전에 사용된 ref 를 제거하고 새 ref 를 설정해야 합니다.
해결 방법 : ref 콜백을 클래스에 바인딩된 메서드로 선언함으로써 해결할 수 있습니다.
* 인라인 함수란?
인라인 함수는 React 가 렌더링 될 때 정의되는 함수를 말합니다.
레거시 API : 문자열 ref
ref 에 접근하기 위해 this.refs.textInput 과 같이 문자열 ref 로 사용하고 있었다면,
콜백 ref 나 createRef() API 를 대신 사용하는 것을 권해드립니다.
* 차후 배포에서 삭제 될 것으로 예상됨.
Ref 정리
1. ref 는 컴포넌트에서 접근할 수 있는 어트리뷰트를 말한다.
2. 리액트에서 DOM 엘리먼트에 접근해야 할 때 사용하면 좋다.
3. 클래스형 컴포넌트에서는 React.createRef() 메서드를, 함수형 컴포넌트에서는 useRef() hook 을 사용할 수 있다.
4. ref 는 createRef() 함수, 콜백 함수, 문자열로 사용할 수 있다.
궁금증?
1. 자식 컴포넌트가 함수 컴포넌트 일 때 왜 동작하지 않는가? 인스턴스가 존재하지 않아서?
하지만 예시에서 DOM 엘리먼트나 클래스 컴포넌트의 인스턴스에 접근 하려고 useRef() 를 통해서 접근하는건 되었다.
함수 컴포넌트 일 때는 ref 가 전달이 되지 않는건가? 그런데 React 16.2 이전 버전에서 forwardRef 메서드 보다 유연한 방법으로 함수형 컴포넌트에서 '자식에게 특별한 props 를 노출 시키고, 자식 요소에 props 를 ref 속성으로 전달했다'
이렇게 사용하는건 괜찮은건가?? 알아보기
'React' 카테고리의 다른 글
Error: Node Sass version 5.0.0 is incompatible with ^4.0.0. (0) | 2021.01.05 |
---|---|
windowing 라이브러리 (0) | 2021.01.05 |
Forwarding Refs (React.forwardRef) (0) | 2020.12.14 |
Context API (0) | 2020.12.10 |
[Errer] name can no longer contain capital letters (0) | 2020.10.28 |