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関数が必要な理由がわかりません。
私は何か間違っていますか?
状態値を新しい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コンポーネントを作成します。
componentDidUpdate()
componentWillReceivePropsではなく現在使用する必要があります
gaearonの記事 も参照してください。
ここには2つの潜在的な問題があります
状態を回避すると、コンストラクターまたはライフサイクルメソッドが不要になります。したがって、コンポーネントをステートレス機能コンポーネントとして作成できるため、この方法でコンポーネントを作成するとパフォーマンス上のメリットがあります。
以下は、コンポーネントから状態を削除し、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);
あなたの場合、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.... }
このようなwithRouter()
を追加する同様の問題がありました:
export default withRouter(connect(mapStateToProps, mapDispatchToProps)(TrajectContainer));
実装の問題は、(小道具から来る)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)