리액트 공식문서 스터디 7일차 - Render Props, Strict Mode

hyebin Jo·2022년 8월 17일
0

Render Props

render props 패턴으로 구현된 컴포넌트는 자체적으로 렌더링 로직을 구현하는 대신, react 엘리먼트 요소를 반환하는 함수를 props로 전달합니다.

📌 Render Props 사용예시

Mouse컴포넌트는 마우스 위치를 추적하는 로직을 캡슐화 한 것이고, MouseTracker컴포넌트는 이 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>
          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 />
      </>
    );
  }
}

마우스 주위에 고양이 그림을 보여주려면 고양이 그림을 렌더링하는 Cat.jsx 컴포넌트를 만들고 MouseWithCat컴포넌트에 렌더링합니다.
그리고 MouseWithCat컴포넌트를 MouseTracker컴포넌트에 렌더링합니다.

import React from "react";

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}>
        <Cat mouse={this.state} />
      </div>
    );
  }
}
class MouseTracker extends React.Component {
  render() {
    return (
      <div>
        <h1>Move the mouse around!</h1>
        <MouseWithCat />
      </div>
    );
  }
}

그러나 이 경우 Mouse 컴포넌트를 사용할 때 마다 별도의 MouseWithSomethingElse 컴포넌트를 만들어야 합니다, 그러므로 MouseWithCat는 아직 정말로 재사용이 가능한게 아닙니다.

🌟여기에 render prop를 사용할 수 있습니다. Mouse 컴포넌트 안에 Cat 컴포넌트를 하드 코딩(hard-coding)해서 결과물을 바꾸는 대신에,
Mouse에게 동적으로 렌더링할 수 있도록 해주는 함수 prop을 제공하는 것입니다. 

class MouseTracker extends React.Component {
  render() {
    return (
      <div>
        <h1>Move the mouse around!</h1>
        <Mouse render={(mouse) => <Cat mouse={mouse} />} />
      </div>
    );
  }
} // MouseTracker는 Mouse컴포넌트에 Cat컴포넌트를 반환하는 함수를 render props로 전달합니다.
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}>
        {this.props.render(this.state)}
      </div>
    );
  } //<Mouse>가 무엇을 렌더링하는지에 대해 명확히 코드로 표기하는 대신,
} //`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 }} />;
  }
}

✅ 정리하자면, render prop은 무엇을 렌더링할지 컴포넌트에 알려주는 함수입니다.

📌 render 이외의 Props 사용법

✔ prop name으로 render를 사용할 필요는 없습니다. 사실, 어떤 함수형 prop이든 render prop이 될 수 있습니다.

<Mouse children={mouse => (//props name으로 꼭 render를 사용할 필요는 없습니다.
  <p>The mouse position is {mouse.x}, {mouse.y}</p>
)}/>

✔ 혹은, 어트리뷰트 이름을 지정해줄 필요 없이, element 안에 직접 꽂아넣을 수 있습니다!

<Mouse>
  {mouse => (
    <p>The mouse position is {mouse.x}, {mouse.y}</p>
  )}
</Mouse>

이 테크닉은 자주 사용되지 않기 때문에, API를 디자인할 때 children은 함수 타입을 가지도록 propTypes를 지정하는 것이 좋습니다.

Mouse.propTypes = {
  children: PropTypes.func.isRequired
};

📌 주의사항

render props 패턴을 사용하면 React.PureComponent를 사용할 때 발생하는 이점이 사라질 수 있습니다.

class Mouse extends React.PureComponent {
  // 위와 같은 구현체...
}

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

이 예시에서 MouseTracker가 render 될때마다, <Mouse render>의 prop으로 넘어가는 함수가 계속 새로 생성됩니다. 따라서 Mouse 컴포넌트가 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>
    );
  }
}

strict 모드

StrictMode는 애플리케이션 내의 잠재적인 문제를 알아내기 위한 도구입니다.

function ExampleApplication() {
  return (
    <div>
      <Header />
      <React.StrictMode> //strictMode 태그로 감싼 컴포넌트만 strice모드가 활성화됩니다.
        <div>
          <ComponentOne />
          <ComponentTwo />
        </div>
      </React.StrictMode>
      <Footer />
    </div>
  );
}

strictMode는 다음과 같은 오류를 잡아냅니다

  • 안전하지 않은 생명주기를 사용하는 컴포넌트 발견
    componentWillMount, componentWillReceiveProps, componentWillUpdate
  • 레거시 문자열 ref 사용에 대한 경고
  • 권장되지 않는 findDOMNode 사용에 대한 경고
  • 예상치 못한 부작용 검사
    다음 함수들을 의도적으로 두 번 호출하여 예상치못한 버그를 발견할 수 있습니다.
    • 클래스 컴포넌트의 constructor, render, shouldComponentUpdate 메서드
    • 클래스 컴포넌트의 getDerivedStateFromProps static 메서드
    • 함수 컴포넌트 바디
    • State updater 함수 (setState의 첫 번째 인자)
    • useState, useMemo, useReducer에 전달되는 함수
  • 레거시 context API 검사

0개의 댓글