setState aktualisiert den Status nicht sofort

102

Ich möchte fragen, warum sich mein Status nicht ändert, wenn ich ein Onclick-Ereignis durchführe. Ich habe vor einiger Zeit gesucht, dass ich die onclick-Funktion im Konstruktor binden muss, aber der Status wird immer noch nicht aktualisiert. Hier ist mein Code:

import React from 'react';

import Grid from 'react-bootstrap/lib/Grid';
import Row from 'react-bootstrap/lib/Row';
import Col from 'react-bootstrap/lib/Col';

import BoardAddModal from 'components/board/BoardAddModal.jsx';

import style from 'styles/boarditem.css';

class BoardAdd extends React.Component {

    constructor(props){
        super(props);

        this.state = {
            boardAddModalShow: false
        }

        this.openAddBoardModal = this.openAddBoardModal.bind(this);
    }
    openAddBoardModal(){
        this.setState({ boardAddModalShow: true });
// After setting a new state it still return a false value
        console.log(this.state.boardAddModalShow);

    }

    render() {

        return (
            <Col lg={3}>
                <a href="javascript:;" className={style.boardItemAdd} onClick={this.openAddBoardModal}>
                    <div className={[style.boardItemContainer,style.boardItemGray].join(' ')}>
                        Create New Board
                    </div>
                </a>



            </Col>
        )
    }
}

export default BoardAdd
Sydney Loteria
quelle
5
Die Antwort, die Sie auf diese Frage akzeptiert haben, macht keinen Sinn. setStategibt kein Versprechen zurück. Wenn es funktioniert, funktioniert es nur , weil awaiteinleitet ein async „Tick“ in die Funktion, und es geschah , dass der Staat Update während dieser Zecke verarbeitet wurde. Es ist nicht garantiert. Wie in dieser Antwort angegeben , müssen Sie den Abschlussrückruf verwenden (wenn Sie nach der Aktualisierung des Status wirklich etwas tun müssen, was ungewöhnlich ist; normalerweise möchten Sie nur neu rendern, was automatisch geschieht).
TJ Crowder
Es wäre gut, wenn Sie die aktuell akzeptierte Antwort nicht akzeptieren oder eine korrekte akzeptieren würden, da diese dann als Duplikat für viele andere Fragen verwendet werden könnte. Eine falsche Antwort oben zu haben, ist irreführend.
Guy Incognito

Antworten:

11

Dieser Rückruf ist wirklich chaotisch. Verwenden Sie stattdessen einfach async await:

async openAddBoardModal(){
    await this.setState({ boardAddModalShow: true });
    console.log(this.state.boardAddModalShow);
}
Spock
quelle
22
Das macht keinen Sinn. React's setStategibt kein Versprechen zurück.
TJ Crowder
16
@TJCrowder ist richtig. setStategibt kein Versprechen zurück, daher sollte es NICHT erwartet werden. Das heißt, ich denke, ich kann sehen, warum dies für einige Leute funktioniert, weil das Warten das Innenleben von setState vor dem Rest der Funktion auf den Aufrufstapel legt, so dass es zuerst verarbeitet wird und somit so aussieht, als wäre der Zustand einstellen. Wenn setState neue asynchrone Aufrufe hätte oder implementiert, würde diese Antwort fehlschlagen. Um diese Funktion ordnungsgemäß zu implementieren, können Sie Folgendes verwenden:await new Promise(resolve => this.setState({ boardAddModalShow: true }, () => resolve()))
Mike Richards
145

Ihr Status benötigt einige Zeit zum Mutieren. Da er console.log(this.state.boardAddModalShow)ausgeführt wird, bevor der Status mutiert, erhalten Sie den vorherigen Wert als Ausgabe. Sie müssen also die Konsole im Rückruf auf die setStateFunktion schreiben

openAddBoardModal() {
  this.setState({ boardAddModalShow: true }, function () {
    console.log(this.state.boardAddModalShow);
  });
}

setStateist asynchron. Dies bedeutet, dass Sie es nicht in einer Leitung anrufen können und davon ausgehen können, dass sich der Status in der nächsten geändert hat.

Laut React docs

setState()mutiert nicht sofort, this.statesondern erzeugt einen ausstehenden Zustandsübergang. Der Zugriff this.statenach dem Aufrufen dieser Methode kann möglicherweise den vorhandenen Wert zurückgeben. Es gibt keine Garantie für den synchronen Betrieb von Aufrufen an setState, und Anrufe können zur Leistungssteigerung gestapelt werden.

Warum sollten sie setStateasynchron machen ?

Dies liegt daran, dass setStatesich der Status ändert und ein erneutes Rendern verursacht wird. Dies kann eine teure Operation sein, und wenn der Browser synchronisiert wird, reagiert er möglicherweise nicht mehr.

Daher sind die setStateAufrufe sowohl asynchron als auch gestapelt, um eine bessere Benutzeroberfläche und Leistung zu erzielen.

Shubham Khatri
quelle
Bis jetzt ist es eine großartige Idee, ABER was ist, wenn wir console.log nicht verwenden können, weil Sie eslint-Regeln verwenden?
Maudev
2
@maudev, eslint-Regeln verhindern, dass Sie console.logs haben, damit sie nicht in der Produktion landen. Das obige Beispiel dient jedoch nur zum Debuggen. und console.log und durch eine Aktion ersetzt werden, die den aktualisierten Status berücksichtigt
Shubham Khatri
1
Was ist, wenn der Rückruf nicht wie gesagt funktioniert? meins nicht!
Alex Jolig
32

Zum Glück setState() nimmt ein Rückruf. Und hier erhalten wir den aktualisierten Status.

Betrachten Sie dieses Beispiel.

this.setState({ name: "myname" }, () => {                              
        //callback
        console.log(this.state.name) // myname
      });

Wenn ein Rückruf ausgelöst wird, ist this.state der aktualisierte Status.
Sie können mutated/updatedDaten im Rückruf erhalten.

Mustkeem K.
quelle
1
Sie können diesen Rückruf auch verwenden, um eine beliebige Funktion zu übergeben initMap(), zum Beispiel
Dende
Gut gemacht! Endlich mein Problem gelöst. Vielen Dank.
Ahmet Halilović
11

Da setSatate eine asynchrone Funktion ist, müssen Sie den Status als solchen Rückruf trösten.

openAddBoardModal(){
    this.setState({ boardAddModalShow: true }, () => {
        console.log(this.state.boardAddModalShow)
    });
}
Adnan shah
quelle
6

setState()aktualisiert die Komponente nicht immer sofort. Das Update kann stapelweise oder später verschoben werden. Dies macht das Lesen von this.state direkt nach dem Aufrufen setState()einer potenziellen Gefahr. Verwenden Sie stattdessen componentDidUpdateoder einen setStateRückruf ( setState(updater, callback)), der nach dem Anwenden des Updates garantiert ausgelöst wird. Wenn Sie den Status basierend auf dem vorherigen Status festlegen müssen, lesen Sie das unten stehende Updater-Argument.

setState()führt immer zu einem erneuten Rendern, es sei denn, es wird shouldComponentUpdate()false zurückgegeben. Wenn veränderbare Objekte verwendet werden und die bedingte Renderlogik nicht implementiert werden kann shouldComponentUpdate(), setState()werden unnötige Neurender vermieden , wenn nur aufgerufen wird , wenn der neue Status vom vorherigen Status abweicht.

Das erste Argument ist eine Updater-Funktion mit der Signatur:

(state, props) => stateChange

stateist eine Referenz auf den Komponentenzustand zum Zeitpunkt der Anwendung der Änderung. Es sollte nicht direkt mutiert werden. Stattdessen sollten Änderungen dargestellt werden, indem ein neues Objekt basierend auf den Eingaben von Status und Requisiten erstellt wird. Angenommen, wir möchten einen Wert im Status um props.step erhöhen:

this.setState((state, props) => {
    return {counter: state.counter + props.step};
});
Aman Seth
quelle
2

Wenn Sie verfolgen möchten, ob der Status aktualisiert wird oder nicht, können Sie dies auch auf andere Weise tun

_stateUpdated(){
  console.log(this.state. boardAddModalShow);
}

openAddBoardModal(){
  this.setState(
    {boardAddModalShow: true}, 
    this._stateUpdated.bind(this)
  );
}

Auf diese Weise können Sie die Methode "_stateUpdated" jedes Mal aufrufen, wenn Sie versuchen, den Status für das Debuggen zu aktualisieren.

Ravin Gupta
quelle
1

setState()ist asynchron. Der beste Weg, um zu überprüfen, ob der Status aktualisiert wird, ist das componentDidUpdate()und kein console.log(this.state.boardAddModalShow)Nach this.setState({ boardAddModalShow: true }).

laut React Docs

Stellen Sie sich setState () als Anforderung und nicht als sofortigen Befehl zum Aktualisieren der Komponente vor. Um die Leistung besser wahrzunehmen, kann React sie verzögern und dann mehrere Komponenten in einem Durchgang aktualisieren. React garantiert nicht, dass die Statusänderungen sofort angewendet werden

stuli
quelle
1

Laut React Docs

React garantiert nicht, dass die Statusänderungen sofort angewendet werden. Dies macht das Lesen von this.state direkt nach dem Aufruf von setState () zu einem potenziellen Problem pitfallund kann den existingWert möglicherweise aufgrund der asyncNatur zurückgeben. Verwenden Sie stattdessen componentDidUpdateoder einen setStateRückruf, der direkt nach erfolgreicher setState-Operation ausgeführt wird. Im Allgemeinen empfehlen wir stattdessen die Verwendung componentDidUpdate()für eine solche Logik.

Beispiel:

import React from "react";
import ReactDOM from "react-dom";

import "./styles.css";

class App extends React.Component {
  constructor() {
    super();
    this.state = {
      counter: 1
    };
  }
  componentDidUpdate() {
    console.log("componentDidUpdate fired");
    console.log("STATE", this.state);
  }

  updateState = () => {
    this.setState(
      (state, props) => {
        return { counter: state.counter + 1 };
      });
  };
  render() {
    return (
      <div className="App">
        <h1>Hello CodeSandbox</h1>
        <h2>Start editing to see some magic happen!</h2>
        <button onClick={this.updateState}>Update State</button>
      </div>
    );
  }
}

const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
Khizer
quelle
0

Für alle, die dies mit Haken versuchen, benötigen Sie useEffect.

function App() {
  const [x, setX] = useState(5)
  const [y, setY] = useState(15) 

  console.log("Element is rendered:", x, y)

  // setting y does not trigger the effect
  // the second argument is an array of dependencies
  useEffect(() => console.log("re-render because x changed:", x), [x])

  function handleXClick() {
    console.log("x before setting:", x)
    setX(10)
    console.log("x in *line* after setting:", x)
  }

  return <>
    <div> x is {x}. </div>
    <button onClick={handleXClick}> set x to 10</button>
    <div> y is {y}. </div>
    <button onClick={() => setY(20)}> set y to 20</button>
  </>
}

Ausgabe:

Element is rendered: 5 15
re-render because x changed: 5
(press x button)
x before setting: 5
x in *line* after setting: 5
Element is rendered: 10 15
re-render because x changed: 10
(press y button)
Element is rendered: 10 20
Luke Miles
quelle
-1

Als ich den Code ausführte und meine Ausgabe an der Konsole überprüfte, zeigte sich, dass er undefiniert ist. Nachdem ich mich umgesehen und etwas gefunden habe, das für mich funktioniert hat.

componentDidUpdate(){}

Ich habe diese Methode in meinem Code nach constructor () hinzugefügt. Überprüfen Sie den Lebenszyklus des reaktiven nativen Workflows.

https://images.app.goo.gl/BVRAi4ea2P4LchqJ8

Pravin Ghorle
quelle
-2
 this.setState({
    isMonthFee: !this.state.isMonthFee,
  }, () => {
    console.log(this.state.isMonthFee);
  })
Atchutha Rama Reddy Karri
quelle
2
Dies ist genau das, was die am besten bewertete Antwort erklärt, aber ohne jede Erklärung und 3 Jahre zu spät. Bitte posten Sie keine doppelten Antworten, es sei denn, Sie haben etwas Relevantes hinzuzufügen.
Emile Bergeron
-2

Ja, da setState eine asynchrone Funktion ist. Der beste Weg, um den Status direkt nach dem Schreiben des Set-Status festzulegen, ist die Verwendung von Object.assign wie folgt: Wenn Sie beispielsweise eine Eigenschaft isValid auf true setzen möchten, gehen Sie folgendermaßen vor


Object.assign (this.state, {isValid: true})


Sie können direkt nach dem Schreiben dieser Zeile auf den aktualisierten Status zugreifen.

Dipanshu Sharma
quelle