Render Props

December 06, 2020

1. Render Prop?

  • props의 이름이 render.
  • render는 해당 컴포넌트의 state를 props로 내린다.
  • render prop은 무엇을 렌더링할지 컴포넌트에 알려주는 함수
<DataProvider render={(data) => <h1>Hello {data.target}</h1>} />

2. 사용하는 이유?

  • React 컴포넌트 간에 코드를 공유하기 위해
  • Cross-Cutting Concerns을 위해 (공통된 로직을 재사용하기 위해)
    • 컴포넌트는 React에서 코드의 재사용성을 위해 사용하는 주요 단위
    • 컴포넌트에서 캡슐화된 상태나 동작을 같은 상태를 가진 다른 컴포넌트와 공유하는 방법이 항상 명확하지 않다.

3. 예제를 통해 이해하자

  • 마우스 위치 추적 컴포넌트
class MouseTracker extends React.Component {
    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 {
          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>
                          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 />
                  </>
              )
          }
      }
  • P 태그가 아닌 고양이 사진을 넣어주려면?

    • 오로직 Cat 컴포넌트만 사용할 뿐 재사용하려면 계속해서 WithSomething을 만들어줘야한다.

      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 {
          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>
              )
          }
      }
  • render Props를 통해 동적으로 렌더링을 할 수 있게 만들어주자

    • Mouse 역할 : 마우스 위치만 건네줄뿐!!! render는 props의 render값이 할 것!

      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 {
          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>
              )
          }
      }

    주의사항

    • React.PureComponent에서 render props pattern을 사용할 땐 주의해주세요.

    • 얕은 prop 비교는 새로운 prop에 대해 항상 false를 반환합니다.

    • 이 경우 render마다 render prop으로 넘어온 값을 항상 새로 생성합니다.

      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>
              )
          }
      }
    • 이 예제에서 가 render 될때마다, 의 prop으로 넘어가는 함수가 계속 새로 생성됩니다. 따라서 React.PureComponent를 상속받은 컴포넌트 효과가 사라지게 됩니다.

      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>
              )
          }
      }

© 2023, Customized by Joon