Reaktionskomponente, die bei Zustandsänderung nicht erneut gerendert wird

Lesezeit: 7 Minuten

Ich habe eine Reaktionsklasse, die an eine API geht, um Inhalte abzurufen. Ich habe bestätigt, dass die Daten zurückkommen, aber es wird nicht erneut gerendert:

var DealsList = React.createClass({
  getInitialState: function() {
    return { deals: [] };
  },
  componentDidMount: function() {
    this.loadDealsFromServer();
  },
  loadDealsFromServer: function() {
    var newDeals = [];

    chrome.runtime.sendMessage({ action: "findDeals", personId: this.props.person.id }, function(deals) {
      newDeals = deals;
    });

    this.setState({ deals: newDeals });
  },
  render: function() {
    var dealNodes = this.state.deals.map(function(deal, index) {
      return (
        <Deal deal={deal} key={index} />
      );
    });
    return (
      <div className="deals">
        <table>
          <thead>
            <tr>
              <td>Name</td>
              <td>Amount</td>
              <td>Stage</td>
              <td>Probability</td>
              <td>Status</td>
              <td>Exp. Close</td>
            </tr>
          </thead>
          <tbody>
            {dealNodes}
          </tbody>
        </table>
      </div>
    );
  }
});

Wenn ich jedoch a hinzufüge debugger Wie unten, newDeals sind ausgefüllt, und wenn ich dann fortfahre, sehe ich die Daten:

  loadDealsFromServer: function() {
    var newDeals = [];

    chrome.runtime.sendMessage({ action: "findDeals", personId: this.props.person.id }, function(deals) {
      newDeals = deals;
    });
    debugger
    this.setState({ deals: newDeals });
  },

Dies ist die Liste der Calling Deals:

var Gmail = React.createClass({
  render: function() {
    return (
      <div className="main">
        <div className="panel">
          <DealsList person={this.props.person} />
        </div>
      </div>
    );
  }
});

Dazu möchte ich noch den enorm simplen, aber ach so leicht gemachten Schreibfehler hinzufügen:

this.state.something = 'changed';

… und dann nicht verstehen, warum es nicht rendert und googelt und auf diese Seite kommt, nur um zu erkennen, dass Sie hätten schreiben sollen:

this.setState({something: 'changed'});

React löst nur dann ein erneutes Rendern aus, wenn Sie es verwenden setState um den Zustand zu aktualisieren.

  • Das ist genau das Problem, das ich hatte. Es ist seltsam, dass sie keine Warnung ausgeben, da sie eine ausgeben, wenn sie versuchen, Requisiten zu aktualisieren.

    – AndrewJM

    13. Dezember 2015 um 22:38 Uhr

  • @AndrewJM Sie können keine Warnung ausgeben. Sie könnten, wenn Sie schreiben würden this.state = 'something' weil Sie den Setter für schlagen würden stateaber im obigen Beispiel trifft der Code auf den Getter, der ein Objekt zurückgibt und dann ein Feld für ein Objekt festlegt, das nur eine Kopie des Zustands ist.

    – Stijn de Witt

    23. Juni 2017 um 21:37 Uhr


  • @TheBigCheese Schreiben Sie besser eine (gute) Frage dazu. Es gibt Leute, die helfen können, aber nicht hier in den Kommentaren einer anderen Frage.

    – Stijn de Witt

    13. November 2019 um 19:17 Uhr

  • @StijndeWitt Ich habe mich bei Stackoverflow angemeldet, um Ihre Antwort zu verbessern. Danke Bruder ;D

    – Esterlinkof

    20. Mai 2020 um 22:10 Uhr

  • Sehr wichtiges Konzept für Anfänger, um es zu verstehen und sich zu merken.

    – Unbesiegbarer Muffi

    29. Juli 2021 um 10:34 Uhr

Mein Szenario war etwas anders. Und ich denke, dass viele Neulinge wie ich ratlos wären – also hier teilen.

Meine Zustandsvariable ist ein Array von JSON-Objekten, die wie folgt mit useState verwaltet werden:

const [toCompare, setToCompare] = useState([]);

Wenn Sie jedoch toCompare mit setToCompare wie in der folgenden Funktion aktualisieren, wird das erneute Rendern nicht ausgelöst. Und das Verschieben auf eine andere Komponente hat auch nicht funktioniert. Nur wenn ein anderes Ereignis ein erneutes Rendern auslösen würde, wurde die aktualisierte Liste angezeigt.

const addUniversityToCompare = async(chiptoadd) =>
  {
      var currentToCompare = toCompare;
      currentToCompare.push(chiptoadd);
      setToCompare(currentToCompare);
  }

Dies war die Lösung für mich. Grundsätzlich – das Zuweisen des Arrays war das Kopieren der Referenz – und React würde das nicht als Änderung sehen – da die Referenz auf das Array nicht geändert wird – nur Inhalt darin. Also im folgenden Code – einfach das Array mit Slice kopiert – ohne Änderung – und es nach Mods wieder zugewiesen. Funktioniert einwandfrei.

const addUniversityToCompare = async (chiptoadd) => {
    var currentToCompare = toCompare.slice();
    currentToCompare.push(chiptoadd);
    setToCompare(currentToCompare);
}

Hoffe es hilft jemandem wie mir. Bitte lassen Sie es mich wissen, wenn Sie der Meinung sind, dass ich falsch liege – oder es einen anderen Ansatz gibt.

Danke im Voraus.

  • Wow, mein Problem war fast eine Kopie. Vielen Dank!

    – stevejboyer

    13. Mai 2021 um 7:23 Uhr

  • Ja ich auch. In ES6 können wir den Spread-Operator verwenden, um den Zustand zu klonen, um React aufzufordern, die Zustandsänderung zu bestätigen: let newItems = [...items];

    – S Meaden

    25. Mai 2021 um 20:20 Uhr

  • Ja Meaden, das ist richtig. Ich stimme zu. Halten Sie es für sinnvoll, meinen Lösungsbeitrag zu aktualisieren, um diesen Punkt als besseren Weg einzufügen, damit die Leute das als allgemeinere Methode zur Umgehung dieses Problems verwenden? Gerne behilflich sein.

    – KManisch

    1. Juni 2021 um 6:51 Uhr

  • haha, auch hier ein Durchschlagsproblem. KOMISCH!

    – Crashtor

    10. August 2021 um 17:28 Uhr

  • Auch hier eine Durchschlagskopie!… halt, am Ende kopieren wir immer den Code von jemand anderem, inklusive deren Probleme 😉

    – Nelson García

    17. Februar um 16:56 Uhr

Benutzer-Avatar
Michelle Tilley

Das liegt daran, dass die Antwort von chrome.runtime.sendMessage ist asynchron; Hier ist die Reihenfolge der Operationen:

var newDeals = [];

// (1) first chrome.runtime.sendMessage is called, and *registers a callback*
// so that when the data comes back *in the future*
// the function will be called
chrome.runtime.sendMessage({...}, function(deals) {
  // (3) sometime in the future, this function runs,
  // but it's too late
  newDeals = deals;
});

// (2) this is called immediately, `newDeals` is an empty array
this.setState({ deals: newDeals });

Wenn Sie das Skript mit dem Debugger anhalten, geben Sie der Erweiterung Zeit, den Rückruf aufzurufen. Bis Sie fortfahren, sind die Daten angekommen und es scheint zu funktionieren.

Um das zu beheben, möchten Sie das tun setState Aufruf, nachdem die Daten von der Chrome-Erweiterung zurückkommen:

var newDeals = [];

// (1) first chrome.runtime.sendMessage is called, and *registers a callback*
// so that when the data comes back *in the future*
// the function will be called
chrome.runtime.sendMessage({...}, function(deals) {
  // (2) sometime in the future, this function runs
  newDeals = deals;

  // (3) now you can call `setState` with the data
  this.setState({ deals: newDeals });
}.bind(this)); // Don't forget to bind(this) (or use an arrow function)

[Edit]

Wenn dies bei Ihnen nicht funktioniert, sehen Sie sich die anderen Antworten auf diese Frage an, die andere Gründe erläutern, warum Ihre Komponente möglicherweise nicht aktualisiert wird.

  • Duh! Ich hätte wissen müssen, dass dies der Fall war, als die Debugger-Zeile es behoben hat. Die Bindung (dies) war das, was ich vermisst habe, als ich das zum ersten Mal versuchte. Danke, toller ausführlicher Kommentar!

    – brandonhilkert

    19. September 2014 um 15:54 Uhr

Ein weiterer ach so einfacher Fehler, der für mich die Ursache des Problems war: Ich hatte meinen eigenen geschrieben shouldComponentUpdate -Methode, die die neue Statusänderung, die ich hinzugefügt hatte, nicht überprüfte.

In meinem Fall habe ich angerufen this.setState({}) korrekt, aber meine Funktion war nicht daran gebunden, also funktionierte es nicht. Hinzufügen .bind(this) zum Funktionsaufruf oder Tun this.foo = this.foo.bind(this) im Konstruktor behoben.

  • Ich habe dieses Problem. Alle Funktionen sind ordnungsgemäß im Konstruktor gebunden.

    – Der große Käse

    12. November 2019 um 15:49 Uhr

Benutzer-Avatar
Gus T Hintern

Mein Problem war, dass ich „React.PureComponent“ verwendet habe, wenn ich „React.Component“ hätte verwenden sollen.

  • Ich habe dieses Problem. Alle Funktionen sind ordnungsgemäß im Konstruktor gebunden.

    – Der große Käse

    12. November 2019 um 15:49 Uhr

Um den Status ordnungsgemäß zu aktualisieren, sollten Sie das Array nicht mutieren. Sie müssen eine Kopie des Arrays erstellen und dann den Zustand mit dem kopierten Array festlegen.

const [deals, setDeals] = useState([]);
    
   function updateDeals(deal) {
      const newDeals = [...deals]; // spreading operator which doesn't mutate the array and returns new array
      newDeals.push(deal);

      // const newDeals = deals.concat(deal); // concat merges the passed value to the array and return a new array
      // const newDeals = [...deals, deal] // directly passing the new value and we don't need to use push
    
      setDeals(newDeals);
    }

  • Warum wird die Komponente durch direkte Mutation nicht erneut gerendert?

    – Thamaraiselvam

    16. April um 16:16 Uhr

  • @Thamaraiselvam Höchstwahrscheinlich liegt es daran, dass die Reaktion das Objekt nur als Zeiger sieht. React kann also nicht erkennen, dass sich das Objekt geändert hat, bevor sich der Zeiger ändert.

    – Sören Jensen

    5. Juni um 12:25 Uhr

1086200cookie-checkReaktionskomponente, die bei Zustandsänderung nicht erneut gerendert wird

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy