Render Props
โrender propโ๋, React ์ปดํฌ๋ํธ ๊ฐ์ ์ฝ๋๋ฅผ ๊ณต์ ํ๊ธฐ ์ํด ํจ์ props๋ฅผ ์ด์ฉํ๋ ๊ฐ๋จํ ํ ํฌ๋์ ๋๋ค.
render props ํจํด์ผ๋ก ๊ตฌํ๋ ์ปดํฌ๋ํธ๋ ์์ฒด์ ์ผ๋ก ๋ ๋๋ง ๋ก์ง์ ๊ตฌํํ๋ ๋์ , react ์๋ฆฌ๋จผํธ ์์๋ฅผ ๋ฐํํ๊ณ ์ด๋ฅผ ํธ์ถํ๋ ํจ์๋ฅผ ์ฌ์ฉํฉ๋๋ค.
<DataProvider render={data => (
<h1>Hello {data.target}</h1>
)}/>
render props๋ฅผ ์ฌ์ฉํ๋ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ React Router, Downshift, Formik์ด ์์ต๋๋ค.
์ด ๋ฌธ์์์๋ render props๊ฐ ์ ์ ์ฉํ๊ณ , ์ด๋ป๊ฒ ์ฌ๋ฌ๋ถ์ ํ๋ก์ ํธ์ ์ ์ฉํ ์ ์์์ง์ ๊ดํด ์ด์ผ๊ธฐ ํ๊ฒ ์ต๋๋ค.
ํก๋จ ๊ด์ฌ์ฌ(Cross-Cutting Concerns)๋ฅผ ์ํ render props ์ฌ์ฉ๋ฒ
์ปดํฌ๋ํธ๋ React์์ ์ฝ๋์ ์ฌ์ฌ์ฉ์ฑ์ ์ํด ์ฌ์ฉํ๋ ์ฃผ์ ๋จ์์ ๋๋ค. ํ์ง๋ง ์ปดํฌ๋ํธ์์ ์บก์ํ๋ ์ํ๋ ๋์์ ๊ฐ์ ์ํ๋ฅผ ๊ฐ์ง ๋ค๋ฅธ ์ปดํฌ๋ํธ์ ๊ณต์ ํ๋ ๋ฐฉ๋ฒ์ด ํญ์ ๋ช ํํ์ง๋ ์์ต๋๋ค.
์๋ฅผ ๋ค๋ฉด, ์๋ ์ปดํฌ๋ํธ๋ ์น ์ ํ๋ฆฌ์ผ์ด์ ์์ ๋ง์ฐ์ค ์์น๋ฅผ ์ถ์ ํ๋ ๋ก์ง์ ๋๋ค.
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>
);
}
}
์คํฌ๋ฆฐ ์ฃผ์๋ก ๋ง์ฐ์ค ์ปค์๋ฅผ ์์ง์ด๋ฉด, ์ปดํฌ๋ํธ๊ฐ ๋ง์ฐ์ค์ (x, y) ์ขํ๋ฅผ <p>
์ ๋ํ๋
๋๋ค.
์ฌ๊ธฐ์ ์ง๋ฌธ์ ๋๋ค. ๋ค๋ฅธ ์ปดํฌ๋ํธ์์ ์ด ํ์๋ฅผ ์ฌ์ฌ์ฉํ๋ ค๋ฉด ์ด๋ป๊ฒ ํด์ผ ํ ๊น์? ์ฆ, ๋ค๋ฅธ ์ปดํฌ๋ํธ์์ ์ปค์(cursor) ์์น์ ๋ํด ์์์ผ ํ ๊ฒฝ์ฐ, ์ฝ๊ฒ ๊ณต์ ํ ์ ์๋๋ก ์บก์ํํ ์ ์์ต๋๊น?
React์์ ์ปดํฌ๋ํธ๋ ์ฝ๋ ์ฌ์ฌ์ฉ์ ๊ธฐ๋ณธ ๋จ์์ด๋ฏ๋ก, ์ฐ๋ฆฌ๊ฐ ํ์๋ก ํ๋ ๋ง์ฐ์ค ์ปค์ ํธ๋ํน ํ์๋ฅผ <Mouse>
์ปดํฌ๋ํธ๋ก ์บก์ํํ์ฌ ์ด๋์๋ ์ฌ์ฉํ ์ ์๊ฒ ๋ฆฌํฉํ ๋ง ํด๋ณด๊ฒ ์ต๋๋ค.
// <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 />
</>
);
}
}
์ด์ <Mouse>
์ปดํฌ๋ํธ๋ ๋ง์ฐ์ค ์์ง์ ์ด๋ฒคํธ๋ฅผ ๊ฐ์งํ๊ณ , ๋ง์ฐ์ค ์ปค์์ (x, y)
์์น๋ฅผ ์ ์ฅํ๋ ํ์๋ฅผ ์บก์ํํ์ต๋๋ค. ๊ทธ๋ฌ๋ ์์ง ์๋ฒฝํ๊ฒ ์ฌ์ฌ์ฉํ ์ ์๋ ๊ฑด ์๋๋๋ค.
์๋ฅผ ๋ค์ด, ๋ง์ฐ์ค ์ฃผ์์ ๊ณ ์์ด ๊ทธ๋ฆผ์ ๋ณด์ฌ์ฃผ๋ <Cat>
์ปดํฌ๋ํธ๋ฅผ ์๊ฐํด ๋ณด๊ฒ ์ต๋๋ค. ์ฐ๋ฆฌ๋ <Cat mouse={{x, y}}>
prop์ ํตํด Cat ์ปดํฌ๋ํธ์ ๋ง์ฐ์ค ์ขํ๋ฅผ ์ ๋ฌํด์ฃผ๊ณ ํ๋ฉด์ ์ด๋ค ์์น์ ์ด๋ฏธ์ง๋ฅผ ๋ณด์ฌ์ค์ง ์๋ ค ์ฃผ๊ณ ์ ํฉ๋๋ค.
์ฒซ ๋ฒ์งธ ๋ฐฉ๋ฒ์ผ๋ก๋, ๋ค์๊ณผ ๊ฐ์ด <Mouse>
์ปดํฌ๋ํธ์ render ๋ฉ์๋์์ <Cat>
์ปดํฌ๋ํธ๋ฅผ ๋ฃ์ด ๋ ๋๋งํ๋ ๋ฐฉ๋ฒ์ด ์์ต๋๋ค.
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(<MouseWithCat>
๊ณผ ๊ทผ๋ณธ์ ์ผ๋ก ๋ค๋ฅธ)๋ฅผ ๋ง๋ค์ด์ผ ํฉ๋๋ค.
์ฌ๊ธฐ์ render prop๋ฅผ ์ฌ์ฉํ ์ ์์ต๋๋ค. <Mouse>
์ปดํฌ๋ํธ ์์ <Cat>
์ปดํฌ๋ํธ๋ฅผ ํ๋ ์ฝ๋ฉ(hard-coding)ํด์ ๊ฒฐ๊ณผ๋ฌผ์ ๋ฐ๊พธ๋ ๋์ ์, <Mouse>
์๊ฒ ๋์ ์ผ๋ก ๋ ๋๋งํ ์ ์๋๋ก ํด์ฃผ๋ ํจ์ prop์ ์ ๊ณตํ๋ ๊ฒ์
๋๋ค.โโโ์ด๊ฒ์ด render 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์ผ๋ก ์ ๋ฌํด์ค์ผ๋ก์จ, <Mouse>
์ปดํฌ๋ํธ๋ ๋์ ์ผ๋ก ํธ๋ํน ๊ธฐ๋ฅ์ ๊ฐ์ง ์ปดํฌ๋ํธ๋ค์ ๋ ๋๋งํ ์ ์์ต๋๋ค.
์ ๋ฆฌํ์๋ฉด, render prop์ ๋ฌด์์ ๋ ๋๋งํ ์ง ์ปดํฌ๋ํธ์ ์๋ ค์ฃผ๋ ํจ์์ ๋๋ค.
์ด ํ
ํฌ๋์ ํ์(๋ง์ฐ์ค ํธ๋ํน ๊ฐ์)๋ฅผ ๋งค์ฐ ์ฝ๊ฒ ๊ณต์ ํ ์ ์๋๋ก ๋ง๋ค์ด ์ค๋๋ค. ํด๋น ํ์๋ฅผ ์ ์ฉํ๋ ค๋ฉด, <Mouse>
๋ฅผ ๊ทธ๋ฆฌ๊ณ ํ์ฌ (x, y) ์ปค์ ์์น์ ๋ฌด์์ ๊ทธ๋ฆด์ง์ ๋ํ ์ ๋ณด๋ฅผ prop์ ํตํด ๋๊ฒจ์ฃผ๊ธฐ๋ง ํ๋ฉด ๋ฉ๋๋ค.
render props์ ๋ํด ํ๊ฐ์ง ํฅ๋ฏธ๋ก์ด ์ ์ ๋๋ถ๋ถ์ higher-order components(HOC)์ render props pattern์ ์ด์ํ ์ ์์ต๋๋ค. ์๋ฅผ ๋ค๋ฉด, <Mouse>
์ปดํฌ๋ํธ๋ณด๋ค withMouse HOC๋ฅผ ๋ ์ ํธํ๋ค๋ฉด render prop์ ์ด์ฉํด์ ๋ค์๊ณผ ๊ฐ์ด ์ฝ๊ฒ HOC๋ฅผ ๋ง๋ค ์ ์์ต๋๋ค.
// ์ด๋ค ์ด์ ๋๋ฌธ์ HOC๋ฅผ ๋ง๋ค๊ธฐ ์ํ๋ค๋ฉด, ์ฝ๊ฒ ๊ตฌํํ ์ ์์ต๋๋ค.
// render prop์ ์ด์ฉํด์ ์ผ๋ฐ์ ์ธ ์ปดํฌ๋ํธ๋ฅผ ๋ง๋์ธ์!
function withMouse(Component) {
return class extends React.Component {
render() {
return (
<Mouse render={mouse => (
<Component {...this.props} mouse={mouse} />
)}/>
);
}
}
}
๋ฐ๋ผ์ render props๋ฅผ ์ฌ์ฉํ๋ฉด ๋ ๊ฐ์ง ํจํด ๋ชจ๋ ์ฌ์ฉ์ด ๊ฐ๋ฅํฉ๋๋ค.
render
์ด์ธ์ Props ์ฌ์ฉ๋ฒ
์ฌ๊ธฐ์ ์ค์ํ๊ฒ ๊ธฐ์ตํด์ผ ํ ๊ฒ์, โrender props patternโ์ผ๋ก ๋ถ๋ฆฌ๋ ์ด์ ๋ก ๊ผญ prop name์ผ๋ก render
๋ฅผ ์ฌ์ฉํ ํ์๋ ์์ต๋๋ค. ์ฌ์ค, ์ด๋ค ํจ์ํ prop์ด๋ render prop์ด ๋ ์ ์์ต๋๋ค.
์ ์์์์๋ render
๋ฅผ ์ฌ์ฉํ์ง๋ง, ์ฐ๋ฆฌ๋ children
prop์ ๋ ์ฝ๊ฒ ์ฌ์ฉํ ์ ์์ต๋๋ค.
<Mouse children={mouse => (
<p>The mouse position is {mouse.x}, {mouse.y}</p>
)}/>
์ค์ ๋ก JSX element์ โ์ดํธ๋ฆฌ๋ทฐํธโ๋ชฉ๋ก์ ํ์ ์ดํธ๋ฆฌ๋ทฐํธ ์ด๋ฆ(์๋ฅผ๋ค๋ฉด render)์ ์ง์ ํ ํ์๋ ์์ต๋๋ค. ๋์ ์, element ์์ ์ง์ ๊ฝ์๋ฃ์ ์ ์์ต๋๋ค!
<Mouse>
{mouse => (
<p>The mouse position is {mouse.x}, {mouse.y}</p>
)}
</Mouse>
์ด ํ ํฌ๋์ react-motion API์์ ์ค์ ๋ก ์ฌ์ฉ๋ ๊ฒ์ ๋ณผ ์ ์์ต๋๋ค.
์ด ํ
ํฌ๋์ ์์ฃผ ์ฌ์ฉ๋์ง ์๊ธฐ ๋๋ฌธ์, API๋ฅผ ๋์์ธํ ๋ children
์ ํจ์ ํ์
์ ๊ฐ์ง๋๋ก propTypes
๋ฅผ ์ง์ ํ๋ ๊ฒ์ด ์ข์ต๋๋ค.
Mouse.propTypes = {
children: PropTypes.func.isRequired
};
์ฃผ์์ฌํญ
React.PureComponent์์ render props pattern์ ์ฌ์ฉํ ๋ ์ฃผ์ํด์ฃผ์ธ์.
render props ํจํด์ ์ฌ์ฉํ๋ฉด React.PureComponent
๋ฅผ ์ฌ์ฉํ ๋ ๋ฐ์ํ๋ ์ด์ ์ด ์ฌ๋ผ์ง ์ ์์ต๋๋ค. ์์ prop ๋น๊ต๋ ์๋ก์ด prop์ ๋ํด ํญ์ false
๋ฅผ ๋ฐํํฉ๋๋ค. ์ด ๊ฒฝ์ฐ render
๋ง๋ค render prop์ผ๋ก ๋์ด์จ ๊ฐ์ ํญ์ ์๋ก ์์ฑํฉ๋๋ค.
์์์ ์ฌ์ฉํ๋ <Mouse>
์ปดํฌ๋ํธ๋ฅผ ์ด์ฉํด์ ์๋ฅผ ๋ค์ด๋ณด๊ฒ ์ต๋๋ค. mouse์ React.Component
๋์ ์ React.PureComponent
๋ฅผ ์ฌ์ฉํ๋ฉด ๋ค์๊ณผ ๊ฐ์ ์ฝ๋๊ฐ ๋ฉ๋๋ค.
class Mouse extends React.PureComponent {
// ์์ ๊ฐ์ ๊ตฌํ์ฒด...
}
class MouseTracker extends React.Component {
render() {
return (
<div>
<h1>Move the mouse around!</h1>
{/*
์ด๊ฒ์ ์ข์ง ์์ต๋๋ค! `render` prop์ด ๊ฐ์ง๊ณ ์๋ ๊ฐ์
๊ฐ๊ฐ ๋ค๋ฅธ ์ปดํฌ๋ํธ๋ฅผ ๋ ๋๋ง ํ ๊ฒ์
๋๋ค.
*/}
<Mouse render={mouse => (
<Cat mouse={mouse} />
)}/>
</div>
);
}
}
์ด ์์์์ <MouseTracker>
๊ฐ render ๋ ๋๋ง๋ค, <Mouse render>
์ prop์ผ๋ก ๋์ด๊ฐ๋ ํจ์๊ฐ ๊ณ์ ์๋ก ์์ฑ๋ฉ๋๋ค. ๋ฐ๋ผ์ React.PureComponent
๋ฅผ ์์๋ฐ์ <Mouse>
์ปดํฌ๋ํธ ํจ๊ณผ๊ฐ ์ฌ๋ผ์ง๊ฒ ๋ฉ๋๋ค.
์ด ๋ฌธ์ ๋ฅผ ํด๊ฒฐํ๊ธฐ ์ํด์, ๋ค์๊ณผ ๊ฐ์ด ์ธ์คํด์ค ๋ฉ์๋๋ฅผ ์ฌ์ฉํด์ prop์ ์ ์ํฉ๋๋ค.
class MouseTracker extends React.Component {
// `this.renderTheCat`๋ฅผ ํญ์ ์์ฑํ๋ ๋งค์๋๋ฅผ ์ ์ํฉ๋๋ค.
// ์ด๊ฒ์ render๋ฅผ ์ฌ์ฉํ ๋ ๋ง๋ค *๊ฐ์* ํจ์๋ฅผ ์ฐธ์กฐํฉ๋๋ค.
renderTheCat(mouse) {
return <Cat mouse={mouse} />;
}
render() {
return (
<div>
<h1>Move the mouse around!</h1>
<Mouse render={this.renderTheCat} />
</div>
);
}
}
prop์ ์ ์ ์ผ๋ก ์ ์ํ ์ ์๋ ๊ฒฝ์ฐ์๋ <Mouse>
์ปดํฌ๋ํธ๋ React.Component
๋ฅผ ์์๋ฐ์์ผ ํฉ๋๋ค.