React - Render props

Sharing data with other components

부모 컴포넌트에서 자식 컴포넌트로 데이터를 전달하기 위해서 props를 사용한다


class Parent extends Component { 
    construcotr(props) {
        super(props);
        this.state = {x: 10, y:20}
    }

    render() {
        return (
            <Child cord={this.state}>
        )
    }
}

Render props

‘리엑트를 다루는 기술’이라는 책을 읽던 중 render props에 대해 나왔다.

단번에 이해가 되지 않아서 공식 문서를 참고해 공부해보았다.

먼저 render propsrender props technique 또는 render props pattern을 말하는 것으로

여기서 render라는 이름은 관습적으로 지칭하는, 함수를 받는 props객체 안 필드를 말한다. (라이프 사이클 함수인 render()와 같은 것이 아니다. render()는 메소드이고 render propsrender는 props필드 이름이다.)

다음 코드를 보면 무슨 말인이 조금 이해가 갈 것이다.

// ParentComponent.js
import React from "react";
import ChildComponent from "./ChildComponent";

const ParentComponent = () => {
  return (
    <div>
      <ChildComponent render={(number) => number + 2} />
    </div>
  );
};

export default ParentComponent;

// ChildComponent.js
import React from 'react';

const ChildComponent = ({render}) => {
    return (
        <div>
            {render(2)} {/* 렌더링 시 화면에 4가 출력된다.*/}
        </div>
    );
};

export default ChildComponent;

사실 보면 알겠지만 함수를 받기 위해 render라는 이름을 굳이 안써도 된다. 하지만 render라고 쓰는 이유는 해당 props의 역할을 명확하게 하기 위해서 인 것 같다.

언제 쓰는 것인가?

다른 컴포넌트에 함수를 넘겨주는 경우는 어떤 경우인가?

다음 코드를 살펴보자 (지금부터 나오는 코드는 React의 공식문서에 나와 있는 render props예시다.)


// Mouse.js ------------------------------------
import React, { Component } from "react";

class Mouse extends Component {
  constructor(props) {
    super(props);
    this.handleMouseMove = this.handleMouseMove.bind(this);
    this.state = { x: 0, y: 0 };
  }

  // 마우스의 움직임을 추적해서 state에 업데이트 한다.
  handleMouseMove(event) {
    this.setState({
      x: event.clientX,
      y: event.clientY,
    });
  }

  render() {
    return (
      <div onMouseMove={this.handleMouseMove}>
        {/* 함수의 인자로 state객체를 넘긴다. */}
        {this.props.render(this.state)}
      </div>
    );
  }
}

export default Mouse;
// MouseTracker.js ------------------------------------
import React, { Component } from "react";
import Mouse from "./Mouse";

class MouseTracker extends Component {
  render() {
    return (
      <>
        <h1>Move the mouse around!</h1>
        <Mouse render={(mouse) => {}} /> {/* 이런 식으로 함수가 전달 된다. */}
      </>
    );
  }
}

export default MouseTracker;

Mouse 컴포넌트는 마우스의 움직임을 추적하여 state객체에 값을 업데이트한다. 그리고 그 값을 props로 받는 render라는 함수에 인자로 넘긴다.

render props를 이용하여 다른 컴포넌트에서 Mouse 컴포넌트에서 구현한 기능(마우스 추적기능)을 사용할 수 있다.

다음 코드를 살펴보자

// ReactLogo.js
import React, { Component } from "react";

class ReactLogo extends Component {
  render() {
    const mouse = this.props.mouse;
    const style = { position: "absolute", left: mouse.x, top: mouse.y}
    return (
      <img
        src="/logo192.png" // react logo
        style={style}
        alt="logo"
      />
    );
  }
}

export default ReactLogo;

위 컴포넌트는 특정 absoluti position에 react 로고를 띄우는 컴포넌트다. mouse라는 props을 받고, 그 객체의 x와 y를 이용하여 로고의 위치를 계산한다. 위 컴포넌트와 mouse컴포넌트의 기능을 결합하기 위해서 render props 테크닉을 이용한다.

MouseTracker.js를 다음과 같이 수정한다.

// MouseTracker.js ------------------------------------
import React, { Component } from "react";
import Mouse from "./Mouse";
import ReactLogo from './ReactLogo';

class MouseTracker extends Component {
  render() {
    return (
      <>
        <h1>Move the mouse around!</h1>
        <Mouse render={(mouse) => {<ReactLogo mouse={mouse} />}} /> 
      </>
    );
  }
}

export default MouseTracker;

마우스 추적 기능을 가진 Mouse 컴포넌트에 그 기능을 이용하여 화면에 렌더링을 수행할 다른 컴포넌트를 결합한다.

props에 함수를 전달함으로써 무엇을 렌더링 할 것인지 결정할 수 있다.

children 을 이용한 render props

다음과 같은 형태도 render props이다.

// import 생략

const MyComponent = ({children}) => {
  return (
    <div>
      {children(2)}
    </div>
  )
}


<MyComponent>{value => 2 * value}</MyComponent>

MyComponent는 컴포넌트 사이에 함수를 받아 인자로 2를 넘긴다.

컴포넌트 사이에 {value => 2 * value}는 인자에 2를 곱해서 반환하는 함수를 넣는다.

해당 함수는 props.children을 통해서 MyComponet로 넘어가서 계산을 수행하여

<div>
  4
</div>

로 computed된다.