setState nie aktualizuje stanu natychmiast

112

Chciałbym zapytać, dlaczego mój stan się nie zmienia, kiedy robię wydarzenie onclick. Szukałem jakiś czas temu, że muszę powiązać funkcję onclick w konstruktorze, ale nadal stan nie jest aktualizowany. Oto mój kod:

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
źródło
5
Odpowiedź , którą zaakceptowałeś na to pytanie, nie ma sensu. setStatenie zwraca obietnicy. Jeśli zadziałało, zadziałało tylko dlatego, że awaitwprowadza do funkcji jeden asynchroniczny „tik” i zdarzyło się, że aktualizacja stanu została przetworzona podczas tego tiku. To nie jest gwarantowane. Jak mówi ta odpowiedź , musisz użyć wywołania zwrotnego zakończenia (jeśli naprawdę musisz coś zrobić po aktualizacji stanu, co jest niezwykłe; zwykle chcesz po prostu ponownie renderować, co dzieje się automatycznie).
TJ Crowder
Byłoby dobrze, gdybyś odrzucił aktualnie zaakceptowaną odpowiedź lub zaakceptowałeś poprawną, ponieważ może ona następnie zostać wykorzystana jako duplikat dla wielu innych pytań. Niepoprawna odpowiedź na górze jest myląca.
Guy Incognito

Odpowiedzi:

12

To oddzwonienie jest naprawdę nieuporządkowane. Zamiast tego użyj async await:

async openAddBoardModal(){
    await this.setState({ boardAddModalShow: true });
    console.log(this.state.boardAddModalShow);
}
Spock
źródło
29
To nie ma sensu. React's setStatenie zwraca obietnicy.
TJ Crowder
17
@TJCrowder ma rację. setStatenie zwraca obietnicy, więc NIE należy jej czekać. To powiedziawszy, myślę, że rozumiem, dlaczego to działa dla niektórych osób, ponieważ await umieszcza wewnętrzne działanie setState na stosie wywołań przed resztą funkcji, więc jest przetwarzane jako pierwsze, a zatem wydaje się, że stan został zestaw. Jeśli setState ma lub zaimplementuje jakiekolwiek nowe wywołania asynchroniczne, ta odpowiedź nie powiedzie się. Aby poprawnie zaimplementować tę funkcję, możesz użyć tego:await new Promise(resolve => this.setState({ boardAddModalShow: true }, () => resolve()))
Mike Richards
Jak do diabła async this.setState({})rozwiązał mój problem? Mieliśmy działający kod, trochę więcej prac programistycznych, ale nic nie zmieniło tej części aplikacji. Tylko jeden z dwóch obiektów w setState był aktualizowany, co sprawiło, że async przywrócił funkcjonalność, a teraz oba obiekty są poprawnie aktualizowane. Nie mam pojęcia, co do cholery było nie tak.
Ondřej Ševčík
Znalazłem to pytanie, ale to rozwiązanie nie zadziałało, nie rozwiązałem jeszcze swojego problemu, ale znalazłem tutaj dobrą lekturę: ozmoroz.com/2018/11/why-my-setstate-doesnt-work
carmolim
151

Twój stan potrzebuje trochę czasu, aby się zmutować, a ponieważ jest console.log(this.state.boardAddModalShow)wykonywany przed mutacją stanu, na wyjściu otrzymujesz poprzednią wartość. Musisz więc napisać konsolę w wywołaniu zwrotnym do setStatefunkcji

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

setStatejest asynchroniczna. Oznacza to, że nie możesz wywołać tego w jednej linii i założyć, że stan zmienił się w następnej.

Według dokumentów React

setState()nie mutuje natychmiast, this.stateale tworzy oczekującą zmianę stanu. Dostęp this.statepo wywołaniu tej metody może potencjalnie zwrócić istniejącą wartość. Nie ma gwarancji synchronicznego działania wywołań setState, a wywołania mogą być grupowane w celu zwiększenia wydajności.

Dlaczego mieliby tworzyć setStateasynchroniczne

Dzieje się tak, ponieważ setStatezmienia stan i powoduje ponowne poddanie się. Może to być kosztowna operacja, a synchronizacja może spowodować, że przeglądarka nie będzie odpowiadać.

W związku z tym setStatewywołania są asynchroniczne, a także grupowane w celu uzyskania lepszego doświadczenia i wydajności interfejsu użytkownika.

Shubham Khatri
źródło
Do tej pory to świetny pomysł, ALE co jeśli nie możemy użyć console.log, ponieważ używasz reguł eslint?
maudev
2
@maudev, reguły eslint uniemożliwiają tworzenie console.logs, aby nie trafiały do ​​produkcji, ale powyższy przykład służy wyłącznie do debugowania. i console.log i zostanie zastąpiona akcją, która uwzględnia zaktualizowany stan
Shubham Khatri
1
Co jeśli oddzwonienie nie działa tak, jak powiedziałeś? mój nie!
Alex Jolig
@ShubhamKhatri to zadziałało świetnie dla mnie, jednak później próbowałem przekazać wartość stanu jako rekwizyt do komponentu podrzędnego, a kiedy console.logustawiam rekwizyt, otrzymuję tę samą wartość, co stan oryginalny.
Archie Margretts
36

Na szczęście setState() odbiera oddzwonienie. I tutaj otrzymujemy zaktualizowany stan.

Rozważmy ten przykład.

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

Dlatego po uruchomieniu wywołania zwrotnego this.state jest zaktualizowanym stanem.
Możesz uzyskać mutated/updateddane w oddzwonieniu.

Mustkeem K
źródło
1
Możesz również użyć tego wywołania zwrotnego do przekazania dowolnej funkcji initMap(), na przykład
Dende
1
Tak trzymać! Wreszcie rozwiązałem mój problem. Wielkie dzięki.
Ahmet Halilović
12

Ponieważ setSatate jest funkcją asynchroniczną, musisz konsolidować stan jako wywołanie zwrotne w ten sposób.

openAddBoardModal(){
    this.setState({ boardAddModalShow: true }, () => {
        console.log(this.state.boardAddModalShow)
    });
}
Adnan shah
źródło
7

setState()nie zawsze od razu aktualizuje komponent. Może zbiorczo lub odroczyć aktualizację na później. To sprawia, że ​​czytanie this.state zaraz po wywołaniu setState()potencjalnej pułapki. Zamiast tego użyj componentDidUpdatelub setStatecallback ( setState(updater, callback)), z których każda z nich zostanie uruchomiona po zastosowaniu aktualizacji. Jeśli chcesz ustawić stan na podstawie poprzedniego stanu, przeczytaj o argumencie aktualizującym poniżej.

setState()zawsze doprowadzi do ponownego renderowania, chyba że shouldComponentUpdate()zwróci false. Jeśli używane są zmienne obiekty i nie można zaimplementować warunkowej logiki renderowania shouldComponentUpdate(), wywołanie setState()tylko wtedy, gdy nowy stan różni się od poprzedniego, pozwoli uniknąć niepotrzebnego ponownego renderowania.

Pierwszy argument to funkcja aktualizująca z podpisem:

(state, props) => stateChange

statejest odniesieniem do stanu komponentu w momencie zastosowania zmiany. Nie należy go bezpośrednio mutować. Zamiast tego zmiany powinny być reprezentowane przez budowanie nowego obiektu na podstawie danych wejściowych ze stanu i właściwości. Na przykład załóżmy, że chcemy zwiększyć wartość stanu przez props.step:

this.setState((state, props) => {
    return {counter: state.counter + props.step};
});
Aman Seth
źródło
2

Jeśli chcesz śledzić, czy stan się aktualizuje, czy nie, innym sposobem zrobienia tego samego jest

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

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

W ten sposób możesz wywołać metodę „_stateUpdated” za każdym razem, gdy próbujesz zaktualizować stan do debugowania.

Ravin Gupta
źródło
1

setState()jest asynchroniczna. Najlepszym sposobem sprawdzenia, czy stan jest aktualizowany, byłoby componentDidUpdate()umieszczenie console.log(this.state.boardAddModalShow)po this.setState({ boardAddModalShow: true }).

według React Docs

Traktuj metodę setState () jako żądanie, a nie natychmiastowe polecenie aktualizacji komponentu. Aby uzyskać lepszą postrzeganą wydajność, React może ją opóźnić, a następnie zaktualizować kilka komponentów w jednym przebiegu. React nie gwarantuje, że zmiany stanu zostaną zastosowane natychmiast

stuli
źródło
1

Według React Docs

React nie gwarantuje, że zmiany stanu zostaną zastosowane natychmiast. To sprawia, że ​​odczyt this.state zaraz po wywołaniu setState () jest potencjałem pitfalli może potencjalnie zwrócić existingwartość ze względu na asyncnaturę. Zamiast tego użyj componentDidUpdatelub setStatewywołania zwrotnego, które jest wykonywane bezpośrednio po pomyślnym wykonaniu operacji setState. Generalnie zalecamy użycie componentDidUpdate()zamiast tego dla takiej logiki.

Przykład:

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
źródło
1

Dla każdego, kto próbuje to zrobić z haczykami, potrzebujesz 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>
  </>
}

Wynik:

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
źródło
-1

kiedy uruchamiałem kod i sprawdzałem moje dane wyjściowe w konsoli, pokazało, że jest niezdefiniowany. Po tym, jak poszukam i znajdę coś, co dla mnie zadziałało.

componentDidUpdate(){}

Dodałem tę metodę w moim kodzie po konstruktorze (). sprawdź cykl życia React Native Workflow.

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

Pravin Ghorle
źródło
-2
 this.setState({
    isMonthFee: !this.state.isMonthFee,
  }, () => {
    console.log(this.state.isMonthFee);
  })
Atchutha rama reddy Karri
źródło
2
Dokładnie to wyjaśnia najbardziej pozytywna odpowiedź, ale bez żadnego wyjaśnienia i 3 lata spóźniona. Nie publikuj zduplikowanych odpowiedzi, chyba że masz coś istotnego do dodania.
Emile Bergeron
-2

Tak, ponieważ setState jest funkcją asynchroniczną. Najlepszym sposobem na ustawienie stanu zaraz po zapisaniu stanu set jest użycie Object. assign w następujący sposób: Na przykład chcesz ustawić właściwość isValid na true, zrób to w ten sposób


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


Możesz uzyskać dostęp do zaktualizowanego stanu zaraz po napisaniu tej linii.

Dipanshu Sharma
źródło
Nie rób tego, mutuje obecny stan, który jest anty-wzorcem.
Emile Bergeron