Reagieren Sie darauf, dass die untergeordnete Komponente nach der Änderung des übergeordneten Status nicht aktualisiert wird

108

Ich versuche, eine nette ApiWrapper-Komponente zu erstellen, um Daten in verschiedenen untergeordneten Komponenten zu füllen. Nach allem, was ich gelesen habe, sollte dies funktionieren: 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')
);

Aus irgendeinem Grund wird die untergeordnete Komponente anscheinend nicht aktualisiert, wenn sich der übergeordnete Status ändert.

Vermisse ich hier etwas?

Vinnie James
quelle

Antworten:

203

Es gibt zwei Probleme mit Ihrem Code.

Der Ausgangszustand Ihrer untergeordneten Komponente wird über Requisiten festgelegt.

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

Zitat aus dieser SO Antwort :

Das Übergeben des Anfangszustands an eine Komponente als propist ein Anti-Pattern, da die getInitialStateMethode (in unserem Fall der Konstruktor) nur beim ersten Rendern der Komponente aufgerufen wird. Nie mehr. Das heißt, wenn Sie diese Komponente erneut rendern und einen anderen Wert als übergeben prop, reagiert die Komponente nicht entsprechend, da die Komponente den Status vom ersten Rendern an beibehält. Es ist sehr fehleranfällig.

Wenn Sie eine solche Situation nicht vermeiden können, besteht die ideale Lösung darin, mit der Methode componentWillReceivePropsnach neuen Requisiten zu suchen.

Durch Hinzufügen des folgenden Codes zu Ihrer untergeordneten Komponente wird Ihr Problem beim erneuten Rendern der untergeordneten Komponente gelöst.

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

Das zweite Problem ist mit dem fetch.

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

Und hier ist eine funktionierende Geige: https://jsfiddle.net/o8b04mLy/

Yadhu Kiran
quelle
1
"Es ist in Ordnung, den Status basierend auf Requisiten zu initialisieren, wenn Sie wissen, was Sie tun." Gibt es noch andere Nachteile beim Festlegen des Status über Requisiten, abgesehen von der Tatsache, dass nextPropkein erneutes Rendern ohne ausgelöst wird componentWillReceiveProps(nextProps)?
Vinnie James
11
Soweit ich weiß, gibt es keine weiteren Nachteile. In Ihrem Fall könnten wir jedoch eindeutig vermeiden, dass sich ein Zustand innerhalb der untergeordneten Komponente befindet. Der Elternteil kann nur Daten als Requisiten übergeben, und wenn der Elternteil mit seinem neuen Status erneut rendert, wird das Kind auch neu rendern (mit neuen Requisiten). Tatsächlich ist es hier nicht notwendig, einen Zustand innerhalb des Kindes aufrechtzuerhalten. Reine Komponenten FTW!
Yadhu Kiran
7
Für alle, die von nun an lesen, überprüfen Sie static getDerivedStateFromProps(nextProps, prevState) reactjs.org/docs/…
GoatsWearHats
4
Gibt es eine andere Lösung als componentWillReceiveProps (), da diese jetzt veraltet ist?
LearningMath
3
@LearningMath Bitte lesen Sie die neuesten React-Dokumente, die über alternative Möglichkeiten sprechen. Möglicherweise müssen Sie Ihre Logik überdenken.
Yadhu Kiran
1

Es gibt einige Dinge, die Sie ändern müssen.

Wenn fetchSie die Antwort erhalten, handelt es sich nicht um einen JSON. Ich habe gesucht, wie ich diesen JSON bekommen kann und habe diesen Link entdeckt .

Auf der anderen Seite müssen Sie denken, dass die constructorFunktion nur einmal aufgerufen wird.

Sie müssen also die Art und Weise ändern, in der Sie die Daten in der <Child>Komponente abrufen .

Hier habe ich einen Beispielcode hinterlassen: https://jsfiddle.net/emq1ztqj/

Ich hoffe das hilft.

Slorenzo
quelle
Danke dir. Wenn Sie sich das Beispiel ansehen, das Sie erstellt haben, scheint es immer noch so, als würde Child niemals aktualisiert. Irgendwelche Vorschläge, wie Sie die Art und Weise ändern können, wie Child die Daten empfängt?
Vinnie James
2
Bist du sicher? Ich sehe, dass die <Child>Komponente die neuen Daten abruft https://jsonplaceholder.typicode.com/posts/1und erneut rendert.
Slorenzo
1
Ja, ich sehe, dass es jetzt unter OSX funktioniert. iOS löste das erneute Rendern im StackExchange-App-Browser nicht aus
Vinnie James