소품 변경시 React 컴포넌트 다시 렌더링


90

컨테이너 구성 요소에서 프레젠테이션 구성 요소를 분리하려고합니다. 나는 SitesTableSitesTableContainer. 컨테이너는 현재 사용자를 기반으로 적절한 사이트를 가져 오기 위해 redux 작업을 트리거하는 역할을합니다.

문제는 컨테이너 구성 요소가 처음에 렌더링 된 후 현재 사용자를 비동기 적으로 가져 오는 것입니다. 이 수단은 컨테이너 구성 요소는 자신의 코드를 다시 실행해야 함을 알고하지 않는 componentDidMount받는 보낼 데이터를 업데이트 할 기능을 SitesTable. props (user) 중 하나가 변경되면 컨테이너 구성 요소를 다시 렌더링해야한다고 생각합니다. 이 작업을 올바르게 수행하려면 어떻게합니까?

class SitesTableContainer extends React.Component {
    static get propTypes() {
      return {
        sites: React.PropTypes.object,
        user: React.PropTypes.object,
        isManager: React.PropTypes.boolean
      }
     }

    componentDidMount() {
      if (this.props.isManager) {
        this.props.dispatch(actions.fetchAllSites())
      } else {
        const currentUserId = this.props.user.get('id')
        this.props.dispatch(actions.fetchUsersSites(currentUserId))
      }  
    }

    render() {
      return <SitesTable sites={this.props.sites}/>
    }
}

function mapStateToProps(state) {
  const user = userUtils.getCurrentUser(state)

  return {
    sites: state.get('sites'),
    user,
    isManager: userUtils.isManager(user)
  }
}

export default connect(mapStateToProps)(SitesTableContainer);

componentDidUpdate와 같은 다른 기능을 사용할 수 있습니다. 또는 props가 변경 될 때 무언가를 실행하려면 componentWillReceiveProps (nextProps)를 찾고있을 것입니다.
thsorens

Prop을 변경하지 않는 경우 SitesTable을 다시 렌더링해야하는 이유는 무엇입니까?
QoP

@QoP 작업이 디스패치되는 경우 애플리케이션 상태 componentDidMountsites노드를 변경 하여 SitesTable. SitesStable의 sites노드가 변경됩니다.
David

오, 알겠습니다. 답을 써 볼게요.
QoP

1
어떻게 기능 컴포넌트에서 이것을 달성하기 위해
yaswanthkoneri

답변:


115

componentDidUpdate방법에 조건을 추가해야 합니다.

예제는 fast-deep-equal객체를 비교하는 데 사용됩니다.

import equal from 'fast-deep-equal'

...

constructor(){
  this.updateUser = this.updateUser.bind(this);
}  

componentDidMount() {
  this.updateUser();
}

componentDidUpdate(prevProps) {
  if(!equal(this.props.user, prevProps.user)) // Check if it's a new user, you can also use some unique property, like the ID  (this.props.user.id !== prevProps.user.id)
  {
    this.updateUser();
  }
} 

updateUser() {
  if (this.props.isManager) {
    this.props.dispatch(actions.fetchAllSites())
  } else {
    const currentUserId = this.props.user.get('id')
    this.props.dispatch(actions.fetchUsersSites(currentUserId))
  }  
}

후크 사용 (React 16.8.0+)

import React, { useEffect } from 'react';

const SitesTableContainer = ({
  user,
  isManager,
  dispatch,
  sites,
}) => {
  useEffect(() => {
    if(isManager) {
      dispatch(actions.fetchAllSites())
    } else {
      const currentUserId = user.get('id')
      dispatch(actions.fetchUsersSites(currentUserId))
    }
  }, [user]); 

  return (
    return <SitesTable sites={sites}/>
  )

}

비교하려는 소품이 객체 또는 배열 인 경우 useDeepCompareEffect대신을 사용해야 합니다 useEffect.


JSON.stringify는 안정적인 경우 (사양에 따라 그렇지 않은 경우) 이러한 종류의 비교에만 사용할 수 있으므로 동일한 입력에 대해 동일한 출력을 생성합니다. 불필요한 재로드를 피하기 위해 사용자 개체의 id 속성을 비교하거나 소품에 userId-s를 전달하고 비교하는 것이 좋습니다.
László Kardinál

4
componentWillReceiveProps 라이프 사이클 메소드는 더 이상 사용되지 않으며 React 17에서 제거 될 가능성이 있습니다. componentDidUpdate와 새로운 getDerivedStateFromProps 메소드의 조합을 사용하는 것이 React 개발 팀에서 제안하는 전략입니다. 더 많은 자신의 블로그 게시물 : reactjs.org/blog/2018/03/27/update-on-async-rendering.html
michaelpoltorak

@QoP 두 번째 예제 인 React Hooks는 user변경 될 때마다 언 마운트하고 다시 마운트 합니까? 이것은 얼마나 비쌉니까?
Robotron

30

ComponentWillReceiveProps()버그와 불일치로 인해 향후 지원이 중단 될 예정입니다. 소품 변화 요소를 재 렌더링을위한 대안 용액을 사용하는 것 ComponentDidUpdate()ShouldComponentUpdate().

ComponentDidUpdate()구성 요소가 업데이트 될 때마다 호출되고 ShouldComponentUpdate()true를 반환하면 ( ShouldComponentUpdate()정의되지 않은 경우 true기본적으로 반환 됨)

shouldComponentUpdate(nextProps){
    return nextProps.changedProp !== this.state.changedProp;
}

componentDidUpdate(props){
    // Desired operations: ex setting state
}

이 동일한 동작은 ComponentDidUpdate()내부에 조건문을 포함하여 메서드 만 사용하여 수행 할 수 있습니다 .

componentDidUpdate(prevProps){
    if(prevProps.changedProp !== this.props.changedProp){
        this.setState({          
            changedProp: this.props.changedProp
        });
    }
}

조건부없이 또는 ShouldComponentUpdate()구성 요소 를 정의하지 않고 상태를 설정하려고 하면 무한히 다시 렌더링됩니다.


2
이 답변 componentWillReceiveProps은 더 이상 사용되지 않을 예정이며 사용에 대해 제안 되기 때문에 (지금 은 최소한) upvoted해야 합니다.
AnBisw

두 번째 형식 (componentDidUpdate 내부의 조건문)은 다른 상태 변경 (예 : 플래시 메시지 닫기)이 계속 발생하기를 원하기 때문에 저에게 효과적입니다.
Little Brain

11

KEY소품으로 변경되는 고유 키 (데이터 조합)를 사용할 수 있으며 해당 구성 요소는 업데이트 된 소품으로 다시 렌더링됩니다.


4
componentWillReceiveProps(nextProps) { // your code here}

나는 그것이 당신이 필요한 이벤트라고 생각합니다. componentWillReceiveProps컴포넌트가 소품을 통해 무언가를받을 때마다 트리거됩니다. 거기에서 당신은 당신이 원하는 것을 할 수 있습니다.


11
componentWillReceivePropsdeprecated *
Maihan Nijat

2

답변을 보는 것이 좋습니다. 을 그것이 당신이하는 일과 관련이 있는지 확인하는 것이 좋습니다. 실제 문제를 이해한다면 비동기 작업을 올바르게 사용하지 않고 redux "store"를 업데이트하면 자동으로 구성 요소가 새로운 소품으로 업데이트됩니다.

이 코드 섹션 :

componentDidMount() {
      if (this.props.isManager) {
        this.props.dispatch(actions.fetchAllSites())
      } else {
        const currentUserId = this.props.user.get('id')
        this.props.dispatch(actions.fetchUsersSites(currentUserId))
      }  
    }

구성 요소에서 트리거하지 않아야하며 첫 번째 요청을 실행 한 후에 처리해야합니다.

redux-thunk 에서이 예제를 살펴보십시오 .

function makeASandwichWithSecretSauce(forPerson) {

  // Invert control!
  // Return a function that accepts `dispatch` so we can dispatch later.
  // Thunk middleware knows how to turn thunk async actions into actions.

  return function (dispatch) {
    return fetchSecretSauce().then(
      sauce => dispatch(makeASandwich(forPerson, sauce)),
      error => dispatch(apologize('The Sandwich Shop', forPerson, error))
    );
  };
}

반드시 redux-thunk를 사용할 필요는 없지만 이와 같은 시나리오를 추론하고 일치하는 코드를 작성하는 데 도움이됩니다.


맞아요. 그러나 makeASandwichWithSecretSauce 구성 요소 에서 정확히 어디에 파견 합니까?
David

관련 예제와 함께 repo에 연결하겠습니다. 앱에서 react-router를 사용합니까?
TameBadger

@David는 또한 그 예제에 대한 링크를 고맙게 생각합니다. 기본적으로 동일한 문제가 있습니다.
SamYoungNY 2015 년

0

사용하기 쉬운 방법은 다음과 같습니다. prop이 업데이트되면 자동으로 컴포넌트를 다시 렌더링합니다.

render {

let textWhenComponentUpdate = this.props.text 

return (
<View>
  <Text>{textWhenComponentUpdate}</Text>
</View>
)

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