web-dev-qa-db-ja.com

componentWillReceivePropsライフサイクルメソッドを使用する場合

React/Reduxが初めてで、状態に問題があります。

TrajectContainer.jsx

class TrajectContainer extends React.Component {
    constructor(props) {
        super(props);
        this.state = {
            trajects: props.trajects,
            onClick: props.onClick
        };
    }

    componentWillReceiveProps(nextProps) {
        console.log('componentWillReceiveProps', nextProps);
        this.setState(nextProps);
    }

    render() {
        // When the componentWillReceiveProps is not present, the this.state will hold the old state
        console.log('rerender', this.state);
        return (<div className="col-md-6">
            <h2>Trajects</h2>
            <button className="btn btn-primary" onClick={this.state.onClick}>Add new Traject</button>
            {this.state.trajects.map(traject => <Traject traject={traject} key={traject.name}/>)}
        </div>)
    }
}

const mapStateToProps = function (store) {
    console.log('mapToStateProps', store);
    return {
        trajects: store.trajects
    };
};

const mapDispatchToProps = function (dispatch, ownProps) {
    return {
        onClick: function () {
            dispatch(addTraject());
        }
    }
};

export default connect(mapStateToProps, mapDispatchToProps)(TrajectContainer);

レデューサーが新しい状態を返すと、コンポーネントは新しいデータで再レンダリングします。

ただし、componentWillReceiveProps関数を削除すると、render()関数は古い状態になります。

MapStateToPropsで受信したデータを確認しましたが、これは新しい新しい状態です。したがって、render関数が新しいデータを受信するためにcomponentWillReceiveProps関数が必要な理由がわかりません。

私は何か間違っていますか?

28
Mazzy

状態値を新しいprops値で更新する場合は、componentWillReceivePropsが必要です。このメソッドは、props値が変更されるたびに呼び出されます。


あなたの場合、なぜこのcomponentWillReceivePropsメソッドが必要ですか?

状態変数に小道具の値を保存し、次のように使用するためです。

this.state.KeyName

そのため、componentWillReceivePropsライフサイクルメソッドを使用して状態値を新しいprops値で更新する必要があります。コンポーネントのprops値のみが更新されますが、自動的に状態は更新されません。状態を更新しない場合、this.stateは常に初期データを保持します。

componentWillReceivePropsは、props値を状態に保存せずに直接使用する場合は必要ありません:

this.props.keyName

現在、reactはrenderメソッド内で更新されたprops値を常に使用し、propsに変更が発生した場合、コンポーネントを新しいpropsで再レンダリングします。

DOC

componentWillReceiveProps()は、マウントされたコンポーネントが新しい小道具を受け取る前に呼び出されます。 propの変更に応じて状態を更新する必要がある場合(たとえば、リセットする場合)、this.propsとnextPropsを比較し、このメソッドでthis.setState()を使用して状態遷移を実行できます。

Reactは、マウント中に初期プロップでcomponentWillReceivePropsを呼び出しません。コンポーネントのプロパティの一部が更新される場合にのみ、このメソッドを呼び出します。

提案:

Props値を状態に保存せずに、this.propsを直接使用して、UIコンポーネントを作成します。

51
Mayank Shukla

更新

componentDidUpdate()

componentWillReceivePropsではなく現在使用する必要があります

gaearonの記事 も参照してください。

ここには2つの潜在的な問題があります

  1. ストアから値を取得してコンポーネントに小道具として返すためにreduxを使用している状態に小道具を再割り当てしないでください

状態を回避すると、コンストラクターまたはライフサイクルメソッドが不要になります。したがって、コンポーネントをステートレス機能コンポーネントとして作成できるため、この方法でコンポーネントを作成するとパフォーマンス上のメリットがあります。

  1. MapDispatcahToPropsを渡すので、ディスパッチでアクションをラップする必要はありません。オブジェクトが渡される場合、その中の各関数はアクション作成者であると想定されます。同じ関数名を持つが、ディスパッチにラップされたすべてのアクション作成者を持つオブジェクトが返されます

以下は、コンポーネントから状態を削除し、reduxストアから返された状態に依存するコードスニペットです。

import React from "react";
import { connect } from "react-redux";

const TrajectContainer = ({ trajects, addTraject }) => (
        <div className="col-md-6">
                <h2>Trajects</h2>
                <button className="btn btn-primary" onClick={addTraject}>Add new Traject</button>
                {trajects.map(traject => <Traject traject={traject} key={traject.name} />)}
        </div>
);

const mapStateToProps = ({ trajects }) => ({ trajects });

export default connect( mapStateToProps, { addTraject })(TrajectContainer);
4
user1095118

あなたの場合、componentWillReceivePropsが必要になり、新しい小道具を受け取ったら状態を更新する必要があります。なぜなら

  • コンストラクターで、次のように状態を宣言しました。ご覧のとおり、渡されたプロップを使用してstateを作成します(これがcomponentWillReceivePropsとそこで更新するロジックを必要とする理由です)

    this.state = {
        trajects: props.trajects,
        onClick: props.onClick
    };
    
  • したがって、小道具を変更すると、componentWillReceivePropsが呼び出されます。コンストラクターは呼び出されません。そのため、変更がコンポーネントの状態になるように状態を再度設定する必要があります。

  • ただし、ロジックは次のようになります。複数回呼び出された場合に状態の更新が繰り返されるのを防ぐことができるような条件付き。

    componentWillReceiveProps(nextProps) {
     console.log('componentWillReceiveProps', nextProps);
     if (this.props !== nextProps) {
      this.setState(nextProps);
     }
    }
    

内容を変更する場合にのみ、小道具を状態に保存する必要があります。しかし、あなたの場合、変更はありません。そのため、状態に保存して使用する代わりに、this.props.trajectsを直接使用できます。この方法でcomponentWillReceivePropsを取り除くことができるので、レンダリング関数は以下のようなものを使用します

{this.props.trajects.map(traject => //what ever is your code.... }
3
Panther

このようなwithRouter()を追加する同様の問題がありました:

export default withRouter(connect(mapStateToProps, mapDispatchToProps)(TrajectContainer));
2

実装の問題は、(小道具から来る)Reduxストアの状態をReact状態(this.state)に複製していることです

あなたの例では、store.trajectsが更新されると、this.props.trajectが更新され、レンダリングメソッドでthis.props.trajectが使用されている場合にのみレンダリングがトリガーされます(そうではありません)。

Renderメソッドでpropの代わりに状態を使用しているため、this.setStateを使用してコンポーネントの状態を手動で変更してレンダリングをトリガーする必要があります。

これは良いパターンではありません。状態を使用せず、次のような小道具を直接使用することをお勧めします。

class TrajectContainer extends React.Component {
    render() {
        return (<div className="col-md-6">
            <h2>Trajects</h2>
            <button className="btn btn-primary" onClick={this.props.onClick}>Add new Traject</button>
        {this.props.trajects.map(traject => <Traject traject={traject} key={traject.name}/>)}
    </div>)
}
}
const mapStateToProps = function (store) {
  console.log('mapToStateProps', store);
  return {
    trajects: store.trajects
  };
};

const mapDispatchToProps = function (dispatch, ownProps) {
   return {
      onClick: function () {
        dispatch(addTraject());
      }
  }
};

export default connect(mapStateToProps, mapDispatchToProps)(TrajectContainer)
1