setState не оновлює стан відразу


103

Я хотів би запитати, чому мій стан не змінюється, коли я роблю подію onclick. Я шукав деякий час тому, що мені потрібно прив'язати функцію onclick у конструкторі, але все ще стан не оновлюється. Ось мій код:

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

5
Відповідь, яку ви прийняли на це питання, не має сенсу. setStateне повертає обіцянку. Якщо це спрацювало, це спрацювало лише тому, що awaitвводить у функцію один асинхронний "галочку", і траплялося, що оновлення стану оброблялося під час цього галочки. Це не гарантовано. Як сказано у цій відповіді , вам потрібно використовувати зворотний виклик завершення (якщо вам дійсно потрібно щось зробити після оновлення стану, що є незвично; зазвичай ви просто хочете повторити візуалізацію, що відбувається автоматично).
TJ Crowder,

Було б добре, якщо б ви не прийняли прийняту в даний час відповідь або прийняли правильну, оскільки це може бути використано як дублікат для багатьох інших питань. Помилка неправильної відповіді вгорі.
Гай Інкогніто

Відповіді:


12

Цей зворотний дзвінок справді безладний. Просто використовуйте async await замість цього:

async openAddBoardModal(){
    await this.setState({ boardAddModalShow: true });
    console.log(this.state.boardAddModalShow);
}

24
В цьому немає сенсу. React's setStateне повертає обіцянки.
TJ Crowder

16
@TJCrowder має рацію. setStateне повертає обіцянку, тому її НЕ слід чекати. Тим не менш, я думаю, я бачу, чому це працює для деяких людей, тому що await ставить внутрішню роботу setState у стек викликів напередодні решти функції, тому вона обробляється першою, і, таким чином, здається, що стан вже був встановити. Якщо setState мав або реалізовував якісь нові асинхронні виклики, ця відповідь буде невдалою. Щоб правильно реалізувати цю функцію, ви можете використовувати це:await new Promise(resolve => this.setState({ boardAddModalShow: true }, () => resolve()))
Mike Richards

Як, блін, async this.setState({})вирішив мою проблему? У нас був робочий код, зроблено ще кілька розробок, але нічого не змінило цю частину програми. Лише один із двох об’єктів у setState оновлювався, що робить його асинхронним поверненням функціональності назад, і тепер обидва об’єкти коректно оновлюються. Я не чудово уявляю, що, біса, було не так.
Ondřej Ševčík

145

Вашому штату потрібно деякий час для мутації, і оскільки console.log(this.state.boardAddModalShow)виконується до того, як стан мутує, ви отримаєте попереднє значення як вихід. Тому вам потрібно написати консоль у зворотному виклику setStateфункції

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

setStateє асинхронним. Це означає, що ви не можете викликати його на одному рядку і припустити, що стан змінився на наступному.

Відповідно до React docs

setState()не негайно мутує, this.stateале створює очікуваний перехід стану. Доступ this.stateпісля виклику цього методу може потенційно повернути існуюче значення. Немає гарантії синхронної роботи дзвінків до setState, і дзвінки можуть бути пакетними для підвищення продуктивності.

Чому вони роблять setStateасинхронізацію

Це тому, що setStateзмінює стан і викликає рендеринг. Це може бути дорогою операцією, і якщо зробити її синхронною, браузер може не реагувати.

Таким чином, setStateдзвінки є асинхронними, а також груповими для кращого досвіду та продуктивності інтерфейсу.


До цього часу це чудова ідея, А що, якщо ми не можемо використовувати console.log, оскільки ви використовуєте правила eslint?
maudev

2
@maudev, правила eslint заважають вам мати console.logs, щоб вони не потрапили у виробництво, але наведений вище приклад призначений виключно для налагодження. і console.log і замінити на дію, яка враховує оновлений стан
Шубхам Хатрі

1
Що робити, якщо зворотний дзвінок не працює, як ви сказали? моя ні!
Alex Jolig

33

На щастя setState() приймає зворотний дзвінок. І саме тут ми отримуємо оновлений стан.

Розглянемо цей приклад.

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

Отже, коли спрацьовує зворотний виклик, this.state є оновленим станом.
Ви можете отримати mutated/updatedдані при зворотному дзвінку.


1
Ви також можете використовувати цей зворотний дзвінок для передачі будь-якої функції, initMap()наприклад
Dende

Шлях! Нарешті вирішив мою проблему. Дуже дякую.
Ахмет Галілович

11

Оскільки setSatate є асинхронною функцією, то вам потрібно втілити стан як зворотний виклик, подібний цьому.

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

6

setState()не завжди відразу оновлює компонент. Він може пакетно або відкласти оновлення на потім. Це робить читання this.state відразу після виклику setState()потенційної пастки. Натомість використовуйте componentDidUpdateабо setStateзворотний дзвінок (setState(updater, callback) ), який гарантовано спрацює після застосування оновлення. Якщо вам потрібно встановити стан на основі попереднього стану, прочитайте про аргумент оновлення нижче.

setState()завжди призведе до повторного відтворення, якщо не shouldComponentUpdate()поверне false. Якщо використовуються змінні об'єкти, а умовна логіка візуалізації не може бути реалізована у shouldComponentUpdate(), викликsetState() лише тоді, коли новий стан відрізняється від попереднього, дозволить уникнути непотрібних повторних рендерів.

Перший аргумент - це функція оновлення з підписом:

(state, props) => stateChange

state- посилання на стан компонента на момент застосування зміни. Його не слід безпосередньо мутувати. Натомість, зміни слід представляти, будуючи новий об’єкт на основі вхідних даних із стану та властивостей. Наприклад, припустимо, ми хотіли збільшити значення в state за допомогою props.step:

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

2

Якщо ви хочете відстежувати стан, який оновлюється, чи ні, тоді інший спосіб зробити те саме

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

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

Таким чином ви можете викликати метод "_stateUpdated" щоразу, коли намагаєтесь оновити стан для налагодження.


1

setState()є асинхронним. Найкращий спосіб перевірити, чи оновлюється стан, буде в, componentDidUpdate()а не ставити console.log(this.state.boardAddModalShow)після this.setState({ boardAddModalShow: true }).

згідно з React Docs

Подумайте про setState () як про запит, а не як негайну команду на оновлення компонента. Для кращої сприйняття продуктивності React може затримати її, а потім оновити кілька компонентів за один прохід. React не гарантує, що зміни стану застосовуються негайно


1

Згідно з React Docs

React не гарантує, що зміни стану застосовуються негайно. Це робить читання this.state відразу після виклику setState () потенційним pitfallі потенційно може повернути existingзначення через asyncприроду. Натомість використовуйтеcomponentDidUpdate або setStateзворотний виклик, який виконується відразу після успішної операції setState. Загалом ми рекомендуємо componentDidUpdate()замість цього використовувати таку логіку.

Приклад:

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);

0

Для тих, хто намагається зробити це за допомогою гачків, вам потрібно 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>
  </>
}

Вихід:

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

-1

коли я запускав код і перевіряв свої результати на консолі, він показував, що він невизначений. Після того, як я шукаю навколо і знаходжу щось, що працювало на мене.

componentDidUpdate(){}

Я додав цей метод у свій код після конструктора (). перевірити життєвий цикл реакції власного робочого процесу.

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


-2
 this.setState({
    isMonthFee: !this.state.isMonthFee,
  }, () => {
    console.log(this.state.isMonthFee);
  })

2
Саме це пояснює найголосніша відповідь, але без будь-яких пояснень та запізнення на 3 роки. Будь ласка, не публікуйте повторювані відповіді, якщо у вас немає чогось відповідного, щоб додати до них.
Еміль Бержерон,

-2

Так, оскільки setState - це асинхронна функція. Найкращий спосіб встановити стан відразу після написання встановленого стану - це використовувати Object.assign так: Наприклад, якщо ви хочете встановити для властивості isValid значення true, зробіть це так


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


Ви можете отримати доступ до оновленого стану відразу після написання цього рядка.


Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.