小道具が変更されたときにReactコンポーネントを再レンダリングする


94

プレゼンテーションコンポーネントをコンテナコンポーネントから分離しようとしています。私はとを持ってSitesTableSitesTableContainerます。コンテナは、現在のユーザーに基づいて適切なサイトをフェッチするためのreduxアクションをトリガーする役割を果たします。

問題は、コンテナコンポーネントが最初にレンダリングされた後、現在のユーザーが非同期にフェッチされることです。これは、コンテナコンポーネントがcomponentDidMount、に送信するデータを更新する関数内のコードを再実行する必要があることを認識していないことを意味しますSitesTable。小道具(ユーザー)の1つが変更されたときに、コンテナーコンポーネントを再レンダリングする必要があると思います。これを正しく行うにはどうすればよいですか?

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などの他の関数、またはおそらく探している関数componentWillReceiveProps(nextProps)を使用できます
thsorens 2016年

小道具を変更しないのに、なぜSitesTableを再レンダリングする必要があるのですか?
QoP 2016年

@QoPでディスパッチさcomponentDidMountれるアクションsitesは、に渡されるアプリケーション状態のノードを変更しますSitesTable。SitesStableのsitesノードが変更されます。
デビッド

ああ、わかった、答えを書くつもりだ。
QoP 2016年

1
機能性成分でこれを達成するためにどのように
yaswanthkoneri

回答:


120

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プロパティを比較するか、propsでuserId-sを渡して、それらを比較することをお勧めします。
ラースロー・レーカイナル2017

4
componentWillReceivePropsライフサイクルメソッドは非推奨であり、React 17で削除される可能性があることに注意してください。componentDidUpdateと新しいgetDerivedStateFromPropsメソッドの組み合わせを使用することは、React開発チームから提案された戦略です。彼らのブログ投稿の詳細:reactjs.org/blog/2018/03/27/update-on-async-rendering.html
michaelpoltorak

@QoP React Hooksを使用した2番目の例では、user変更が行われるたびにアンマウントおよび再マウントされますか?これはどれくらい高いですか?
ロボトロン

32

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非推奨になりつつあり、使用しないように提案されているため、(少なくとも今のところは)賛成する必要があります。
AnBisw 2018

2番目の形式(componentDidUpdate内の条件ステートメント)は、フラッシュメッセージを閉じるなど、他の状態変更を引き続き発生させたいため、機能します。
リトルブレイン

誰かが私のコードでshouldComponentUpdateを使用していましたが、問題の原因が理解できなかったため、問題が明らかになりました。
stevemoretz20年

14

KEY小道具によって変化する一意のキー(データの組み合わせ)を使用でき、そのコンポーネントは更新された小道具で再レンダリングされます。


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

それがあなたが必要とするイベントだと思います。componentWillReceivePropsコンポーネントが小道具を介して何かを受け取るたびにトリガーされます。そこからチェックをして、やりたいことを何でもすることができます。


12
componentWillReceiveProps非推奨*
Maihan Nijat 2018

3

私のこの答えを見て、それがあなたがしていることに関連しているかどうかを確認することをお勧めします。私があなたの本当の問題を理解しているなら、それはあなたがあなたの非同期アクションを正しく使用しておらず、reduxの「ストア」を更新しているということです。

コードのこのセクション:

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 、コンポーネントのどこに正確にディスパッチしますか?
デビッド

関連する例を使用してリポジトリにリンクします。アプリでreact-routerを使用していますか?
TameBadger 2016年

@Davidもその例へのリンクをいただければ幸いです。基本的に同じ問題があります。
SamYoungNY 2017

0

使いやすい方法は次のとおりです。propが更新されると、コンポーネントが自動的に再レン​​ダリングされます。

render {

let textWhenComponentUpdate = this.props.text 

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

}
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.