Ref는 render 메서드에서 생성된 DOM 노드나 React 엘리먼트에 접근하는 방법을 제공합니다.
일반적으로 React의 데이터 흐름에서 props는 부모 컴포넌트가 자식과 상호작용할 수 있는 유일한 수단이다. 그러나 가끔은 일반적인 흐름을 벗어나서 직접적으로 자식을 수정해야 하는 경우도 있다. 수정할 자식은 React 컴포넌트의 인스턴스일 수도 있으며, DOM 엘리먼트일 수도 있다.
React.createRef()
render 메서드 안에서 ref가 엘리먼트에게 전달되었을 때, 그 노드를 향한 참조는 ref의 current 어트리뷰트에 담기게 된다.
const node = this.myRef.current;
ref의 값은 노드의 유형에 따라 다르다.
const divRef = createRef()
return () {
<div ref={divRef}></div>
}
컴포넌트가 마운트될 때 React는 current 프로퍼티에 DOM 엘리먼트를 대입한다.
컴포넌트의 마운트가 해제될 때 current 프로퍼티를 다시 null로 돌려 놓는다.
class CustomTextInput extends Component {
constructor(props) {
super(props);
this.textInput = React.createRef();
}
focusTextInput = () => {
this.textInput.current.focus();
};
render() {
return (
<div>
<input type="text" ref={this.textInput} />
<input
type="button"
value="Focus the text input"
onClick={this.focusTextInput}
/>
</div>
);
}
}
export default CustomTextInput;
클릭하면 focus 한다.
export default class AutoFocusTextInput extends Component {
constructor(props) {
super(props);
this.textInput = React.createRef();
}
componentDidMount() {
// CustomTextInput 컴포넌트의 인스턴스에 접근, 직접 focusTextInput 메서드를 호출한다.
this.textInput.current.focusTextInput();
}
render() {
return <CustomTextInput ref={this.textInput} />;
}
mount되고 바로 focus 한다.
위 코드는 반드시 클래스 컴포넌트여야 한다.
부모 컴포넌트에서 자식 컴포넌트의 DOM 노드에 접근하려 하는 경우도 있다. 이는 컴포넌트의 캡슐화를 파괴하기에 권장되지는 않지만 가끔가다 자식 컴포넌트의 DOM 노드를 포커스하는 일이나, 크기 또는 위치를 계산하는 일 등을 할 때에는 효과적인 방법이 될 수 있다.
React 16.3 이후 버전을 사용한다면 forwardRef가 효과적인 방법이 될 수 있다.
React 16.2 이전 버전을 사용한다면 ref props로 넘기기를 확인하자.
콜백 ref는 ref 어트리뷰트에 React.createRef()를 통해 생성된 ref 대신, 함수를 전달한다!
class CustomTextInput extends Component {
constructor(props) {
super(props);
this.textInput = null;
this.setTextInputRef = (e) => (this.textInput = e);
this.focusTextInput = () => this.textInput && this.textInput.focus();
}
componentDidMount = () => this.focusTextInput();
render() {
return (
<div>
<input type="text" ref={this.setTextInputRef} />
<input
type="button"
value="Focus the text input"
onClick={this.focusTextInput}
/>
</div>
);
}
}
React 컴포넌트 간에 코드를 공유하기 위해 함수 props를 이용하는 간단한 테크닉이다.
import React from "react";
class Cat extends React.Component {
render() {
const mouse = this.props.mouse;
return (
<p style={{ position: "absolute", left: mouse.x, top: mouse.y }}>
고양이
</p>
);
}
}
class Mouse extends React.Component {
constructor(props) {
super(props);
this.state = { x: 0, y: 0 };
}
handleMouseMove = (event) => {
this.setState({
x: event.clientX,
y: event.clientY,
});
};
render() {
return (
<div style={{ height: "100vh" }} onMouseMove={this.handleMouseMove}>
{/*
<Mouse>가 무엇을 렌더링하는지에 대해 명확히 코드로 표기하는 대신,
`render` prop을 사용하여 무엇을 렌더링할지 동적으로 결정할 수 있다!
*/}
{this.props.render(this.state)}
</div>
);
}
}
class MouseTracker extends React.Component {
render() {
return (
<div>
<h1>Move the mouse around!</h1>
<Mouse render={(mouse) => <Cat mouse={mouse} />} />
</div>
);
}
}
export default MouseTracker;
render에 다른 동물 컴포넌트를 넘겨주기만 하면 된다.
render props pattern으로 불린다고 해서 prop name이 render일 필요는 없다.
class Mouse extends React.Component {
constructor(props) {
super(props);
this.state = { x: 0, y: 0 };
}
handleMouseMove = (event) => {
this.setState({
x: event.clientX,
y: event.clientY,
});
};
render() {
return (
<div style={{ height: "100vh" }} onMouseMove={this.handleMouseMove}>
{this.props.children(this.state)}
</div>
);
}
}
// ...
<Mouse>
{(mouse) => (
<p style={{ position: "absolute", left: mouse.x, top: mouse.y }}>
고양이
</p>
)}
</Mouse>
다만 ... 이런 테크닉은 자주 사용되지 않기에 children은 함수 타입을 가지도록 propsTypes를 지정하자.
React.PropTypes는 React v15.5부터 다른 패키지로 이동하였습니다. 대신 prop-types 라이브러리를 사용하시길 바랍니다. prop-types
이렇게.. 주요 개념과 고급 안내서까지 정독했다.🤸♂️ 생각보다 몰랐던 점이 많았다는게 놀랍기도 하면서 당연하기도...(읽어본적이 없으니)
API참고서와 HOOK이 남았다. 이 부분까지 몰랐던 점을 적을지 말지는 고민을 해봐야겠다.. 슬쩍 봤는데 벌써 모르는게 나오긴 하는것이 고놈 참!
아무튼 재밌었음!