728x90
Render Props란?
- "render prop"란, React 컴포넌트 간에 코드를 공유하기 위해 함수 porps를 이용하는 간단한 테그닉이다.
- render props를 사용하는 라이브러리는 React Router, Downshift, Formik가 있다.
횡단 관심사(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>
);
}
}
// 캡슐화작업
// <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 />
</>
);
}
}
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>
);
}
}
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>
//render함수에 prop으로 전당해줌으로써, <Mouse> 컴포넌트는 동적으로 트래깅 기능을 가진
//컴포넌트들을 렌더링 할 수 있다.
//"render props pattern"으로 불리는 이유로 꼭 prop name으로 render를 사용할 필요없음
<Mouse render={mouse => (
<Cat mouse={mouse} />
)}/>
</div>
);
}
}
render 이외의 Props 사용법
- "render props pattern"으로 불리는 이유로 꼭 prop name으로 render를 사용할 필요 없다
- 위 예시에서는 render를 사용했지만, 우리는 childern prop을 더 쉽게 사용할 수 있다.
<Mouse children={mouse => (
<p>The mouse position is {mouse.x}, {mouse.y}</p>
)}/>
- 실제로 JSX element의 "어트리뷰트"목록에 하위 어트리뷰트 이름(ex: 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.PureComponernt에서 render props pattern을 사용할 땐 주의 할 점
React.PureComponent란?
- React.PureComponent는 React.Component와 비슷하다.
- React.Component는 shouldComponentUpdate()를 구현하지 않지만, React.PureComponent는 props와 state를 이용한 얕은 비교를 구현한다는 차이점만이 존재
/*
<MouseTracker>가 render될때마다, <Mouse render>의 prop으로 넘어가는 함수가 계속 새로 생성
따라서 React.PureComponent를 상속받은 <Mouse>컴포넌트 효과가 사라지게 된다.
*/
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>
);
}
}
/*
이 문제를 해결하기 위해서 다음과 같이 인스턴스 메서드를 사용해서 prop을 정의
prop을 정적으로 정의 할수 없는 경우에는 <Mouse> 컴포넌트는 React.Componenr를 상속
*/
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>
);
}
}728x90
'프로그래밍 > react' 카테고리의 다른 글
| [react] Form (0) | 2023.05.19 |
|---|---|
| [react] 리스트와 Key (0) | 2023.05.18 |
| [react] 조건부 렌더링 (0) | 2023.05.17 |
| [react] 이벤트 처리하기 (0) | 2023.05.17 |
| [react] State (0) | 2023.05.16 |