기능적 상태 비 저장 구성 요소, PureComponent, 구성 요소; 차이점은 무엇이며 언제 사용해야합니까?


189

에서 알고 온 v15.3.0 반응 , 우리는 새로운 기본 클래스라는이 PureComponent 로 확장 할 수 PureRenderMixin 내장을. 내가 이해하는 것은 후드 아래에서 소품의 얕은 비교를 사용한다는 것입니다 shouldComponentUpdate.

이제 React 컴포넌트를 정의하는 3 가지 방법이 있습니다 :

  1. 클래스를 확장하지 않는 기능적인 Stateless 구성 요소
  2. PureComponent클래스 를 확장하는 구성 요소
  3. Component클래스 를 확장하는 일반적인 구성 요소

얼마 전 우리는 무 상태 구성 요소를 순수 구성 요소 또는 멍청한 구성 요소라고 부릅니다. "순수"라는 단어의 전체 정의가 이제 React에서 변경된 것 같습니다.

이 세 가지의 기본적인 차이점을 이해하지만 언제 무엇을 선택할지 잘 모르겠습니다 . 또한 각각의 성능 영향과 장단점은 무엇입니까?


업데이트 :

이것들은 내가 분명히 할 것으로 예상되는 질문입니다.

  • 간단한 구성 요소를 기능적 (단순화를 위해) 또는 PureComponent클래스를 확장 (성능을 위해)으로 정의해야합니까?
  • 내가 잃어버린 단순성과 실제로 균형을 이루는 성능 향상이 있습니까?
  • 더 나은 성능을 위해 Component항상 사용할 수있을 때 정규 수업 을 확장해야 PureComponent합니까?

답변:


315

구성 요소의 목적 / 크기 / 소품 / 동작에 따라이 세 가지 중에서 어떻게 결정합니까?

사용자 지정 방법으로 확장 React.PureComponent하거나 확장하면 성능에 영향을 미칩니다. 상태 비 저장 기능 구성 요소를 사용하는 것은 "아키텍처"선택이며 아직 성능상의 이점이 없습니다.React.ComponentshouldComponentUpdate

  • 쉽게 재사용해야하는 간단한 프리젠 테이션 전용 구성 요소의 경우 상태 비 저장 기능 구성 요소를 선호합니다. 이렇게하면 실제 앱 로직에서 분리되어 테스트하기가 쉽고, 예상치 못한 부작용이 없는지 확인할 수 있습니다. 어떤 이유로 든 많은 이유가 있거나 렌더링 방법을 최적화 해야하는 경우 ( shouldComponentUpdate상태 비 저장 기능 구성 요소를 정의 할 수 없기 때문에)는 예외입니다 .

  • 확장 PureComponent당신이 당신의 출력은 간단한 소품에 의존 알고있는 경우 / 상태 ( "간단한"얕은가 PureComponent의 수행으로, 중첩 된 데이터 구조를 비교 의미가없는) 당신이 필요 / 약간의 성능 향상을 얻을 수 있습니다.

  • 다음 / 현재 소품과 상태간에 사용자 지정 비교 논리를 수행하여 성능 향상이 필요한 경우 Component직접 확장 하고 구현하십시오 shouldComponentUpdate. 예를 들어 lodash # isEqual을 사용하여 빠른 비교를 신속하게 수행 할 수 있습니다.

    class MyComponent extends Component {
        shouldComponentUpdate (nextProps, nextState) {
            return !_.isEqual(this.props, nextProps) || !_.isEqual(this.state, nextState);
        }
    }

또한 자체 구현 shouldComponentUpdate또는 확장 PureComponent은 최적화이며 평소와 같이 성능 문제가있는 경우에만 조사를 시작해야합니다 ( 조기 최적화를 피하십시오 ). 경험상 필자는 대부분의 기능이 이미 구현 된 상태에서 응용 프로그램이 작동 상태 인 후에 항상 이러한 최적화를 수행하려고합니다. 실제로 문제가 발생했을 때 성능 문제에 집중하는 것이 훨씬 쉽습니다.

자세한 내용은

기능적인 상태 비 저장 구성 요소 :

이것들은 단지 함수를 사용하여 정의됩니다. 상태 비 저장 구성 요소의 내부 상태가 없으므로 출력 (렌더링 된 내용)은이 함수에 입력으로 지정된 소품에만 의존합니다.

장점 :

  • React에서 컴포넌트를 정의하는 가장 간단한 방법. 상태를 관리 할 필요가 없다면 왜 클래스와 상속을 방해합니까? 함수와 클래스의 주요 차이점 중 하나는 함수를 사용하면 출력이 입력에만 의존한다는 것입니다 (이전 실행 기록이 아님).

  • 앱에서 이상적인 상태 비 저장 구성 요소를 최대한 많이 확보하는 것이 이상적입니다. 일반적으로 뷰 레이어 외부에서 로직을 이동하고이를 redux와 같은 것으로 이동했기 때문에 아무것도 렌더링하지 않고도 실제 로직을 테스트 할 수 있습니다. (테스트하기가 쉽고, 재사용이 가능합니다.)

단점 :

  • 수명주기 방법이 없습니다. componentDidMount다른 친구 를 정의 할 수있는 방법이 없습니다 . 일반적으로 계층 구조에서 상위 구성 요소 내에서이를 수행하므로 모든 하위 항목을 상태 비 저장 구성 요소로 바꿀 수 있습니다.

  • 을 정의 할 수 없으므로 다시 렌더링이 필요한시기를 수동으로 제어 할 수있는 방법이 없습니다 shouldComponentUpdate. 다시 렌더링은 구성 요소가 새 소품을받을 때마다 발생합니다 (얕은 비교 등). 앞으로 React는 상태 비 저장 구성 요소를 자동으로 최적화 할 수있게되었으므로 현재 사용할 수있는 라이브러리가 몇 가지 있습니다. 상태 비 저장 구성 요소는 단순한 기능이므로 기본적으로 "기능 메모"의 고전적인 문제입니다.

  • 참조는 지원되지 않습니다 : https://github.com/facebook/react/issues/4936

PureComponent 클래스 VS를 확장하는 컴포넌트 VS Component 클래스를 확장하는 일반적인 컴포넌트 :

구식은 구문을 PureRenderMixin사용하여 정의 된 클래스에 첨부 할 수 있도록하는 데 사용됩니다 React.createClass. 믹스 인은 단순히 shouldComponentUpdate다음 소품과 다음 상태 간의 얕은 비교를 수행하여 변경된 것이 있는지 확인합니다. 아무것도 변경되지 않으면 다시 렌더링 할 필요가 없습니다.

ES6 구문을 사용하려는 경우 믹스 인을 사용할 수 없습니다. 따라서 편의상 React는 PureComponent을 사용하는 대신 상속 할 수 있는 클래스를 도입했습니다 Component. PureComponent그냥 shouldComponentUpdate같은 방식으로 구현 합니다 PureRendererMixin. 현재 / 다음 상태와 소품 사이의 얕은 비교가 아마도 당신에게 빠른 성능 승리를 줄 수있는 가장 일반적인 시나리오이기 때문에 주로 편리한 일이므로 직접 구현할 필요가 없습니다.

예:

class UserAvatar extends Component {
    render() {
       return <div><img src={this.props.imageUrl} /> {{ this.props.username }} </div>
    }
} 

보시다시피 출력은 props.imageUrl및에 의존합니다 props.username. 상위 컴포넌트 <UserAvatar username="fabio" imageUrl="http://foo.com/fabio.jpg" />에서 동일한 소품으로 렌더링 하는 render경우 출력이 정확히 동일하더라도 React는 매번 호출 합니다. React는 dom diffing을 구현하므로 DOM은 실제로 업데이트되지 않습니다. 여전히 dom diffing을 수행하는 것은 비용이 많이들 수 있으므로이 시나리오에서는 낭비입니다.

UserAvatar구성 요소가 PureComponent대신 확장 되면 얕은 비교가 수행됩니다. 그리고 props와 nextProps가 동일하기 때문에 render전혀 호출되지 않습니다.

React에서 "pure"정의에 대한 참고 사항 :

일반적으로 "순수 함수"는 동일한 입력이 주어지면 항상 동일한 결과로 평가되는 함수입니다. 출력 (React의 경우 render메소드 가 리턴하는 결과 )은 히스토리 / 상태에 의존하지 않으며 부작용 (기능 외부에서 "세계"를 변경하는 조작)이 없습니다.

React에서 상태 비 저장 구성 요소는 호출 this.setState하지 않고 사용하지 않는 구성 요소를 "상태 비 저장"으로 호출하는 경우 위 정의에 따라 반드시 순수한 구성 요소 는 아닙니다 this.state.

실제로에서는 PureComponent수명주기 방법 중에 부작용을 계속 수행 할 수 있습니다. 예를 들어 내부에 ajax 요청을 보내 componentDidMount거나 DOM 계산을 수행하여 div의 높이를 동적으로 조정할 수 있습니다 render.

"Dumb components"정의는 더 "실용적인"의미를 가지고 있습니다. (적어도 내가 이해한다면) 바보 같은 컴포넌트는 props를 통해 부모 컴포넌트가해야 할 일을 "알고", 일을하는 방법을 모르지만 props를 사용합니다 대신 콜백.

"스마트"의 예 AvatarComponent:

class AvatarComponent extends Component {
    expandAvatar () {
        this.setState({ loading: true });
        sendAjaxRequest(...).then(() => {
            this.setState({ loading: false });
        });
    }        

    render () {
        <div onClick={this.expandAvatar}>
            <img src={this.props.username} />
        </div>
    }
}

"벙어리"의 예 AvatarComponent:

class AvatarComponent extends Component {
    render () {
        <div onClick={this.props.onExpandAvatar}>
            {this.props.loading && <div className="spinner" />}
            <img src={this.props.username} />
        </div>
    }
}

결국 "dumb", "stateless"및 "pure"는 대부분 사용 사례에 따라 때때로 겹칠 수 있지만 반드시 그런 것은 아닙니다.


1
귀하의 답변과 귀하가 공유 한 지식에 감사드립니다. 하지만 내 진짜 질문은 언제 무엇을 선택해야 하는가입니다. . 답변에서 언급 한 것과 동일한 예를 어떻게 정의해야합니까? 기능이있는 상태 비 저장 구성 요소 (그렇다면 왜?)이거나 PureComponent를 확장하거나 (why?) 구성 요소 클래스를 확장해야합니다 (이유는?). 구성 요소 의 목적 / 크기 / 소품 / 동작따라이 세 가지 중에서 어떻게 결정 합니까?
Yadhu Kiran

1
문제 없어요. 기능적인 상태 비 저장 구성 요소의 경우 적절한 것으로 판단 할 수있는 장단점 목록이 있습니다. 그게 당신에게 먼저 대답합니까? 나는 선택 문제를 조금 더 다루려고 노력할 것입니다.
fabio.sussetto

2
기능 구성 요소는 전혀 사용하지 않더라도 상위 구성 요소가 업데이트 될 때 항상 다시 렌더링됩니다 props. .
AlexM

1
이것은 꽤 오랫동안 읽은 가장 포괄적 인 답변 중 하나입니다. 훌륭한 일. 첫 번째 문장에 대한 한 가지 의견 : 확장 할 때을 PureComponent구현해서는 안됩니다 shouldComponentUpdate(). 실제로 이렇게하면 경고가 표시됩니다.
jjramos

1
실제 성능을 향상 시키려면 PureComponent중첩 된 객체 / 배열 속성이있는 구성 요소 에 사용해야 합니다. 물론 무슨 일이 일어나고 있는지 알아야합니다. 내가 올바르게 이해한다면, 소품 / 상태를 직접 변경하지 않거나 (React가 경고로 방해하지 않도록 시도합니다) 외부 라이브러리를 사용하면 거의 모든 곳 PureComponent대신에 잘 사용해야합니다 Component... 예외 실제로 사용하지 않을 수있는 매우 간단한 구성 요소의 예 -news.ycombinator.com/item?id=14418576
Matt Browne

28

나는 천재적으로 반응하지 않지만 다음과 같은 상황에서 각 구성 요소를 사용할 수 있습니다.

  1. 상태 비 저장 구성 요소- 수명주기가없는 구성 요소이므로 해당 구성 요소는 정보 만 표시하고 수행 할 작업이없는 텍스트 목록 렌더링과 같은 상위 구성 요소의 반복 요소를 렌더링하는 데 사용해야합니다.

  2. 순수한 구성 요소- 수명 주기가있는 항목이며 특정 소품 세트가 주어지면 항상 동일한 결과를 반환합니다. 이러한 구성 요소는 복잡한 자식 요소가없고 결과에만 영향을주는 작업을 수행하는 데 사용되는 결과 또는 특정 개체 데이터를 표시 할 때 사용할 수 있습니다. 사용자 카드 또는 제품 카드 목록 (기본 제품 정보) 표시와 사용자가 수행 할 수있는 작업 만 클릭하면 세부 정보 페이지를 보거나 장바구니에 추가 할 수 있습니다.

  3. 일반 구성 요소 또는 복합 구성 요소- 복잡한 구성 요소라는 용어는 일반적으로 페이지 수준 구성 요소이며 많은 하위 구성 요소로 구성되어 있으며 각 하위 구성 요소는 고유 한 방식으로 작동 할 수 있으므로 100 % 확신 할 수 없기 때문에 복합 구성 요소라는 용어를 사용했습니다. 주어진 상태에서 동일한 결과를 렌더링합니다. 내가 일반적으로 말했듯이 이들은 컨테이너 구성 요소로 사용해야합니다


1
이 방법은 효과가 있지만 큰 성능 향상을 놓칠 수 있습니다. PureComponent루트 계층 구성 요소 및 계층 구조 맨 위의 구성 요소를 사용하면 일반적으로 성능이 가장 크게 향상됩니다. 물론 순수 구성 요소가 올바르게 작동하기 위해서는 props를 변경하지 말고 상태를 직접 지정해야하지만 개체를 ​​직접 변경하는 것은 React의 반 패턴입니다.
Matt Browne

5
  • React.Component기본 "일반"구성 요소입니다. class키워드 및을 사용하여 선언합니다 extends React.Component. 라이프 사이클 메소드, 이벤트 핸들러 및 기타 메소드가있는 클래스로 생각하십시오.

  • React.PureComponent있는 React.Component그 구현 shouldComponentUpdate()의의 단순 비교를 수행하는 기능 propsstate. forceUpdate()구성 요소에 변경 사항이 포함 된 props 또는 state 중첩 데이터가 있고 다시 렌더링하려는 경우 사용해야 합니다. 따라서 배열이나 객체가 소품으로 전달되거나 상태가 변경 될 때 다시 렌더링하기 위해 구성 요소가 필요한 경우에는 좋지 않습니다.

  • 기능적 구성 요소는 수명주기 기능이없는 구성 요소입니다. 그것들은 아마도 상태가 없을 것 같지만, 우리가 지금 (10.1 이후부터) 고리를 가지고 있기 때문에 상태가 좋을 정도로 멋지고 깨끗합니다. 그래서 그것들은 단지 "깨끗한 구성 요소"일뿐입니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.