親の状態変更後に子コンポーネントが更新されないことに反応


109

さまざまな子コンポーネントにデータを入力するためのApiWrapperコンポーネントを作成しようとしています。私が読んだすべてから、これはうまくいくはずです:https//jsfiddle.net/vinniejames/m1mesp6z/1/

class ApiWrapper extends React.Component {

  constructor(props) {
    super(props);

    this.state = {
      response: {
        "title": 'nothing fetched yet'
      }
    };
  }

  componentDidMount() {
    this._makeApiCall(this.props.endpoint);
  }

  _makeApiCall(endpoint) {
    fetch(endpoint).then(function(response) {
      this.setState({
        response: response
      });
    }.bind(this))
  }

  render() {
    return <Child data = {
      this.state.response
    }
    />;
  }
}

class Child extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      data: props.data
    };
  }

  render() {
    console.log(this.state.data, 'new data');
    return ( < span > {
      this.state.data.title
    } < /span>);
  };
}

var element = < ApiWrapper endpoint = "https://jsonplaceholder.typicode.com/posts/1" / > ;

ReactDOM.render(
  element,
  document.getElementById('container')
);

しかし、何らかの理由で、親の状態が変化しても子コンポーネントが更新されていないようです。

ここで何か不足していますか?

回答:


203

コードには2つの問題があります。

子コンポーネントの初期状態は、小道具から設定されます。

this.state = {
  data: props.data
};

このSO回答からの引用:

初期状態をコンポーネントとしてに渡すことpropはアンチパターンです。これは、getInitialState(この場合はコンストラクター)メソッドがコンポーネントが初めてレンダリングされるときにのみ呼び出されるためです。これ以上はありません。つまり、異なる値をとして渡してそのコンポーネントを再レンダリングしたprop場合、コンポーネントは最初にレンダリングされたときの状態を維持するため、コンポーネントはそれに応じて反応しません。エラーが発生しやすくなります。

したがって、そのような状況を回避できない場合、理想的な解決策は、メソッドcomponentWillReceivePropsを使用して新しい小道具をリッスンすることです。

以下のコードを子コンポーネントに追加すると、子コンポーネントの再レンダリングに関する問題が解決します。

componentWillReceiveProps(nextProps) {
  this.setState({ data: nextProps.data });  
}

第二の問題は、ですfetch

_makeApiCall(endpoint) {
  fetch(endpoint)
    .then((response) => response.json())   // ----> you missed this part
    .then((response) => this.setState({ response }));
}

そして、これが実用的なフィドルです:https//jsfiddle.net/o8b04mLy/


1
「それはしても大丈夫です小道具に基づいて状態を初期化しているという事実のそばに、小道具を経由して状態を設定する他の欠点はありますあなたは何をやっている知っていれば」nextPropなしで文句を言わないトリガー-再レンダリングcomponentWillReceiveProps(nextProps)
Vinnie James

11
私の知る限り、他の欠点はありません。しかし、あなたのケースでは、子コンポーネント内に状態を持つことを明らかに回避できます。親はデータを小道具として渡すだけで、親が新しい状態で再描画すると、子も再描画されます(新しい小道具で)。ここでは実際に子供の内部の状態を維持する必要はありません。純粋なコンポーネントFTW!
Yadhu Kiran 2016

7
これから読む人は、static getDerivedStateFromProps(nextProps, prevState) reactjs.org
docs /…を

4
現在非推奨になっているため、componentWillReceiveProps()以外の解決策はありますか?
LearningMath

3
@LearningMathは、代替方法について述べている最新のReactドキュメントを参照してください。ロジックを再検討する必要があるかもしれません。
Yadhu Kiran 2018

1

変更する必要があることがいくつかあります。

fetch応答を受け取ったとき、それはjsonではありません。このjsonを取得する方法を探していたところ、このリンクを発見しました。

反対に、constructor関数は一度だけ呼び出されると考える必要があります。

したがって、<Child>コンポーネント内のデータを取得する方法を変更する必要があります。

ここに、サンプルコードを残しました:https : //jsfiddle.net/emq1ztqj/

お役に立てば幸いです。


ありがとうございました。ただし、作成した例を見ると、Childが更新しないように見えます。子供がデータを受け取る方法を変更する方法について何か提案はありますか?
Vinnie James

2
本気ですか?私はそれを参照<Child>コンポーネントから新しいデータを取得https://jsonplaceholder.typicode.com/posts/1し、再レンダリングします。
slorenzo 2016

1
はい、OSXで動作しています。iOSがStackExchangeアプリのブラウザーで再レンダリングをトリガーしなかった
Vinnie James
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.