5주차- REACT - 3 - Ref
in Study-siotz on Topic
5주차 topic -
Ref(Reference 참조)와 DOM
prop으로 사용할 수 없는 것에는 key와 ref가 있었습니다. 기억나나요?
Ref는 render 메소드에서 생성된 DOM 노드 혹은 React 엘리먼트 객체에 접근할 수 있는 방법을 제공합니다.
전형적인 React 데이터 흐름에서는 부모 컴포넌트에서 자식 엘리먼트를 조작하기 위해 props만을 사용합니다. 하지만 가끔은 자식을 명령형으로 변경해야 할 필요가 있습니다. 그러면 어떠한 상황에 DOM에 직접적인 접근이 필요할까요?
1. 언제 ref를 사용해야 하나요?
- input/textarea 등에 포커스를 해야할 때
- 포커스, 텍스트 선택영역, 혹은 미디어의 재생을 관리할 때
- DOM API 메소드로만 가능
- 명령형 애니메이션을 발동시킬 때
- 특정 DOM의 크기를 가져와야 할 때
- 특정 DOM에서 스크롤 위치를 가져오거나 설정을 해야 할 때
- 외부 라이브러리(플레이어, 차트 등)를 사용 할 떄
2. Ref의 남용은 금물입니다.
ref를 사용하기 전에, 앱의 상태를 컴포넌트 계층의 어떤 부분에서 소유해야 하는지를 다시 한 번 생각해보세요. 많은 경우 적절한 장소는 계층의 상위에 있는 컴포넌트일 것입니다.
ref를 사용하면 코드를 간결하게 짤 수 있지만, state와 props로 해결할 수 있는 부분에서는 ref를 사용하지 않는 것이 유지보수에 좋습니다.
3. Ref 사용하기
ref를 사용하는 방법에는 세가지가 있습니다.
1. 문자열 attribute 사용
- 구식 방법이라 잘 사용하지 않습니다만 간단하게 예제로 확인하겠습니다.
class RefExample extends React.Component {
render() {
return (
<div>
<input ref = 'myInput' />
</div>
);
}
componentDidMount() {
this.refs.myInput.value = 'Hi, I used ref to do this.';
this.refs.myInput.placeholder = 'input placeholder';
}
}
ReactDOM.render(<RefExample />, document.getElementById('root'));
- 컴퍼넌트에
ref = 'refName'형식으로 ref를 지정합니다. - 문자열 형식으로 만든 ref는
this.refs.refName으로 접근해야 합니다. - refs를 사용할 때는 컴퍼넌트가 렌더링 된 이후여야 합니다.
2. 콜백 함수 사용
class RefExample extends React.Component {
handleClick() {
// 3. textBox의 input의 value 값을 설정해주는 함수입니다.
this._textBox._input.value = "I used ref";
}
render() {
return (
<div>
// 2. TexoBox _textBox이라는 이름의 ref를 붙여줍니다.
<TextBox ref={box => this._textBox = box} />
<button onClick={this.handleClick.bind(this)}>Click Me</button>
</div>
);
}
}
class TextBox extends React.Component {
render() {
return (
// 1. input에 _input이라는 이름의 ref를 붙여줍니다.
<input ref= { ref => this._input = ref} />
);
}
}
const rootElement = document.getElementById("root");
ReactDOM.render(<RefExample />, rootElement);
3. React.createRef() 사용
- React 16.3 버전에서 도입된 기능입니다.
Ref 생성하기
Ref는 React.creatRef()를 통해 생성한 뒤 React 엘리먼트의 ref 어트리뷰트에 붙여줄 수 있습니다.
class MyComponent extends React.Component {
constructor(props) {
super(props);
// ref 생성
this.myRef = React.createRef();
}
render() {
// 엘리먼트의 ref 어트리뷰트에 붙여줌
return <div ref={this.myRef} />;
}
}
React.createRef() 라는 함수를 실행하면 객체가 생성되고, 이 객체는 붙여준 DOM 객체 가리킵니다(화살표 참조합니다)
Ref 사용하기
render 메소드에서 반환하는 엘리먼트에 ref가 넘겨지면(어트리뷰트에 붙여주어 연결해주면), ref의 current 속성을 통해 해당 노드에 접근할 수 있게 됩니다(연결된 DOM 노드를 가져올 수 있습니다)
const node = this.myRef.curret
ref의 값은 노드의 유형에 따라 달라집니다.
HTML 엘리먼트에
ref어트리뷰트가 사용되면, ref의current속성은 DOM 엘리먼트 객체를 가리킵니다.클래스 컴포넌트에
ref어트리뷰트가 사용되면, ref의current속성은 해당 컴포넌트로부터 생성된 인스턴스를 가리킵니다.(클래스 컴포넌트를 생성하면 자동으로 인스턴스가 생성되고this는 이 인스턴스를 기리킵니다.)함수형 컴포넌트는 인스턴스를 가질 수 없기 때문에 ref 어트리뷰트 역시 사용할 수 없습니다
DOM 엘리먼트에 ref 사용하기
class CustomTextInput extends React.Component {
constructor(props) {
super(props);
// input DOM 엘리먼트에 접근하기 위해 ref를 만들었습니다.
this.textInput = React.createRef();
// bind를 이용해서 this 고정
this.focusTextInput = this.focusTextInput.bind(this);
}
focusTextInput() {
// DOM API를 사용해서 명시적으로 input에 포커스를 두는 코드입니다.
// 주의: "current" 속성을 사용해 DOM 노드에 접근하고 있습니다.
this.textInput.current.focus();
}
render() {
return (
// <input> ref와 `textInput`을 연결해주었습니다.
<div>
<input
type="text"
ref={this.textInput} />
<input
type="button"
value="Focus the text input"
onClick={this.focusTextInput}
/>
</div>
);
}
}
React는 컴포넌트가 마운트 되면 textInput의 current 속성에 DOM 엘리먼트 객체를 할당하며, 언마운트가 되었을 떄 다시 null로 되돌립니다. 이는 인스턴스가 저장된 경우의 메모리 누수를 방지합니다.
ref의 갱신은 componentDidMount와 componentDidUpdate 라이프사이클 훅 직전에 일어납니다.
클래스 컴포넌트에 ref 사용하기
class AutoFocusTextInput extends React.Component {
constructor(props) {
super(props);
// ref를 생성했습니다.
this.CustomInput = React.createRef();
}
componentDidMount() {
// CustomInput ref를 가진 인스턴스의 focusTextInput() 함수를 실행합니다.
this.CustomInput.current.focusTextInput();
}
render() {
return (
// CustomInput ref를 연결해주었습니다.
<CustomTextInput ref={this.CustomInput} />
);
}
}
DOM 엘리먼트에 ref 사용하기 파트의 예제에 있는 CustomTextInput 컴포넌트를 감싸는 새 컴포넌트를 만들었습니다.
CustomTextInput 인스턴스에 접근하기 위해 ref를 생성해 연결했고, focusTextInput을 직접 호출했습니다.
컴포넌트의 인스턴스를 가리키기 위해 ref를 사용하는 것은 컴포넌트가 클래스로 선언되었을 때에만 동작합니다.
함수형 컴포넌트는 인스턴스를 가질 수 없기 때문입니다.
다만, 함수형 컴포넌트 안에서 ref 어트리뷰트를 사용하는 것은 얼마든지 가능합니다.
function CustomTextInput(props) {
// textInput은 반드시 여기에서 선언되어야 합니다.
let textInput = React.createRef();
function handleClick() {
textInput.current.focus();
}
return (
<div>
<input
type="text"
ref={textInput} />
<input
type="button"
value="Focus the text input"
onClick={handleClick}
/>
</div>
);
}
