React 컴포넌트 간에 코드를 공유하기 위해 함수 props를 이용하는 방법
render props 패턴으로 구현된 컴포넌트는 자체적으로 렌더링 로직을 구현하는 대신, React 엘리먼트 요소를 반환하고 이를 호출하는 함수를 사용.
<DataProvider render={data => (
<h1>Hello {data.target}</h1>
)}/>
class MouseTracker extends React.Component {
constructor(props) {
super(props);
this.handleMouseMove = this.handleMouseMove.bind(this);
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}>
<h1>Move the mouse around!</h1>
<p>The current mouse position is ({this.state.x}, {this.state.y})</p>
</div>
);
}
}
마우스를 위치를 찍는 이 컴포넌트를 재사용하기 위해서 어떻게 해야할까?
이게 핵심
// <Mouse> 컴포넌트는 우리가 원하는 행위를 캡슐화 합니다...
class Mouse extends React.Component {
constructor(props) {
super(props);
this.handleMouseMove = this.handleMouseMove.bind(this);
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}>
{/* ...하지만 <p>가 아닌 다른것을 렌더링하려면 어떻게 해야 할까요? */}
<p>The current mouse position is ({this.state.x}, {this.state.y})</p>
</div>
);
}
}
class MouseTracker extends React.Component {
render() {
return (
<>
<h1>Move the mouse around!</h1>
<Mouse />
</>
);
}
}
이제 x,y위치를 저장하는 행위를 캡슐화 했다. 하지만 완벽하지 않다.
예를 들어, 마우스 주위에 고양이 그림을 보여주는 <Cat>
컴포넌트를 생각해보면 prop으로 알려주고자 할 것이다.
class Cat extends React.Component {
render() {
const mouse = this.props.mouse;
return (
<img src="/cat.jpg" style={{ position: 'absolute', left: mouse.x, top: mouse.y }} />
);
}
}
class MouseWithCat extends React.Component {
constructor(props) {
super(props);
this.handleMouseMove = this.handleMouseMove.bind(this);
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}>
{/*
여기서 <p>를 <Cat>으로 바꿀 수 있습니다. ... 그러나 이 경우
Mouse 컴포넌트를 사용할 때 마다 별도의 <MouseWithSomethingElse>
컴포넌트를 만들어야 합니다, 그러므로 <MouseWithCat>는
아직 정말로 재사용이 가능한게 아닙니다.
*/}
<Cat mouse={this.state} />
</div>
);
}
}
class MouseTracker extends React.Component {
render() {
return (
<div>
<h1>Move the mouse around!</h1>
<MouseWithCat />
</div>
);
}
}
하지만 위 컴포넌트는 특정 사례에는 적용할 수 있지만 원하는 행위의 캡슐화(마우스 트래킹)라는 목표는 이루지 못했다. 다른 예시의 사용을 위해 새로운 component를 만들어야 한다.
이 때 render prop을 사용할 수 있다. <Mouse>
컴포넌트안에 <Cat>
컴포넌트를 하드 코딩하는 대신에 동적으로 렌더링할 수 있도록 prop을 제공하는 것이다.
class Cat extends React.Component {
render() {
const mouse = this.props.mouse;
return (
<img src="/cat.jpg" style={{ position: 'absolute', left: mouse.x, top: mouse.y }} />
);
}
}
class Mouse extends React.Component {
constructor(props) {
super(props);
this.handleMouseMove = this.handleMouseMove.bind(this);
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>
);
}
}
즉, render prop은 무엇을 렌더링할지 컴포넌트에 알려주는 함수입니다.
이 방식의 또 장점은 HOC에 render prop 이식할 수 있다는 점이다.
// 어떤 이유 때문에 HOC를 만들기 원한다면, 쉽게 구현할 수 있습니다.
// render prop을 이용해서 일반적인 컴포넌트를 만드세요!
function withMouse(Component) {
return class extends React.Component {
render() {
return (
<Mouse render={mouse => (
<Component {...this.props} mouse={mouse} />
)}/>
);
}
}
}